mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-03-29 16:26:41 +01:00
history checkpoint
This commit is contained in:
parent
d037666ad1
commit
087c0c4f1f
@ -104,8 +104,13 @@ function createWindow(size : {width : number, height : number}) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (input.code == "KeyR" && input.meta && input.alt) {
|
||||
createRemotesWindow();
|
||||
//if (input.code == "KeyR" && input.meta && input.alt) {
|
||||
// createRemotesWindow();
|
||||
// e.preventDefault();
|
||||
// return;
|
||||
//}
|
||||
if (input.code == "KeyH" && input.meta) {
|
||||
win.webContents.send("h-cmd", mods);
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
75
src/main.tsx
75
src/main.tsx
@ -8,9 +8,9 @@ import dayjs from 'dayjs'
|
||||
import {If, For, When, Otherwise, Choose} from "tsx-control-statements/components";
|
||||
import cn from "classnames"
|
||||
import {TermWrap} from "./term";
|
||||
import type {SessionDataType, LineType, CmdDataType, RemoteType, RemoteStateType, RemoteInstanceType, RemotePtrType} from "./types";
|
||||
import type {SessionDataType, LineType, CmdDataType, RemoteType, RemoteStateType, RemoteInstanceType, RemotePtrType, HistoryItem} from "./types";
|
||||
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
||||
import {GlobalModel, GlobalInput, Session, Cmd, Window, Screen, ScreenWindow, riToRPtr} from "./model";
|
||||
import {GlobalModel, GlobalCommandRunner, Session, Cmd, Window, Screen, ScreenWindow, riToRPtr} from "./model";
|
||||
|
||||
dayjs.extend(localizedFormat)
|
||||
|
||||
@ -441,6 +441,10 @@ class TextAreaInput extends React.Component<{}, {}> {
|
||||
}
|
||||
if (e.code == "Enter") {
|
||||
e.preventDefault();
|
||||
if (inputModel.historyShow.get()) {
|
||||
inputModel.grabSelectedHistoryItem();
|
||||
return;
|
||||
}
|
||||
if (!ctrlMod) {
|
||||
setTimeout(() => GlobalModel.inputModel.uiSubmitCommand(), 0);
|
||||
return;
|
||||
@ -459,7 +463,16 @@ class TextAreaInput extends React.Component<{}, {}> {
|
||||
inputModel.clearCurLine();
|
||||
return;
|
||||
}
|
||||
if (e.code == "KeyR" && e.getModifierState("Control")) {
|
||||
e.preventDefault();
|
||||
GlobalCommandRunner.openHistory();
|
||||
return;
|
||||
}
|
||||
if (e.code == "ArrowUp" || e.code == "ArrowDown") {
|
||||
if (inputModel.historyShow.get()) {
|
||||
inputModel.moveHistorySelection(e.code == "ArrowUp" ? -1 : 1);
|
||||
return;
|
||||
}
|
||||
let linePos = this.getLinePos(e.target);
|
||||
if (e.code == "ArrowUp") {
|
||||
if (!lastHist && linePos.linePos > 1) {
|
||||
@ -484,7 +497,11 @@ class TextAreaInput extends React.Component<{}, {}> {
|
||||
}
|
||||
if (e.code == "PageUp" || e.code == "PageDown") {
|
||||
e.preventDefault();
|
||||
let infoScroll = GlobalModel.inputModel.hasScrollingInfoMsg();
|
||||
if (inputModel.historyShow.get()) {
|
||||
inputModel.moveHistorySelection(e.code == "PageUp" ? -10 : 10);
|
||||
return;
|
||||
}
|
||||
let infoScroll = inputModel.hasScrollingInfoMsg();
|
||||
if (infoScroll) {
|
||||
let div = document.querySelector(".cmd-input-info");
|
||||
let amt = pageSize(div);
|
||||
@ -529,23 +546,44 @@ class TextAreaInput extends React.Component<{}, {}> {
|
||||
|
||||
@mobxReact.observer
|
||||
class HistoryInfo extends React.Component<{}, {}> {
|
||||
lastClickHNum : string = null;
|
||||
lastClickTs : number = 0;
|
||||
|
||||
componentDidMount() {
|
||||
let inputModel = GlobalModel.inputModel;
|
||||
let selNum = inputModel.historySelectedNum.get();
|
||||
if (selNum != null) {
|
||||
let elem = document.querySelector(".cmd-history .hnum-" + selNum);
|
||||
if (elem != null) {
|
||||
elem.scrollIntoView({block: "nearest"});
|
||||
}
|
||||
inputModel.scrollHistoryItemIntoView(selNum);
|
||||
}
|
||||
}
|
||||
|
||||
renderHItem(hitem : HistoryItem, selNum : number) : any {
|
||||
@boundMethod
|
||||
handleItemClick(hitem : HistoryItem) {
|
||||
let inputModel = GlobalModel.inputModel;
|
||||
let selNum = inputModel.historySelectedNum.get();
|
||||
if (this.lastClickHNum == hitem.historynum && selNum == hitem.historynum) {
|
||||
inputModel.grabSelectedHistoryItem();
|
||||
return;
|
||||
}
|
||||
inputModel.focusCmdInput();
|
||||
inputModel.setHistorySelectionNum(hitem.historynum);
|
||||
let now = Date.now();
|
||||
this.lastClickHNum = hitem.historynum;
|
||||
this.lastClickTs = now;
|
||||
setTimeout(() => {
|
||||
if (this.lastClickTs == now) {
|
||||
this.lastClickHNum = null;
|
||||
this.lastClickTs = 0;
|
||||
}
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
renderHItem(hitem : HistoryItem, selNum : string) : any {
|
||||
let lines = hitem.cmdstr.split("\n");
|
||||
let line : string = "";
|
||||
let idx = 0;
|
||||
return (
|
||||
<div key={hitem.historynum} className={cn("history-item", {"is-selected": selNum == hitem.historynum}, "hnum-" + hitem.historynum)}>
|
||||
<div key={hitem.historynum} className={cn("history-item", {"is-selected": selNum == hitem.historynum}, "hnum-" + hitem.historynum)} onClick={() => this.handleItemClick(hitem)}>
|
||||
<div className="history-line">{(selNum == hitem.historynum ? "*" : " ")}{sprintf("%5s", hitem.historynum)} {lines[0]}</div>
|
||||
<For each="line" index="index" of={lines.slice(1)}>
|
||||
<div key={idx} className="history-line">{line}</div>
|
||||
@ -553,17 +591,23 @@ class HistoryInfo extends React.Component<{}, {}> {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
handleClose() {
|
||||
GlobalModel.inputModel.toggleInfoMsg();
|
||||
}
|
||||
|
||||
render() {
|
||||
let inputModel = GlobalModel.inputModel;
|
||||
let idx : number = 0;
|
||||
let hitems : HistoryItem[] = inputModel.historyItems.get() ?? [];
|
||||
let selNum = inputModel.historySelectedNum.get();
|
||||
let hitems = inputModel.getFilteredHistoryItems();
|
||||
hitems = hitems.slice().reverse();
|
||||
let hitem : HistoryItem = null;
|
||||
return (
|
||||
<div className="cmd-history">
|
||||
<div className="history-title">
|
||||
showing history for
|
||||
history
|
||||
{" "}
|
||||
<span className="term-bright-white">[containing '']</span>
|
||||
{" "}
|
||||
@ -574,6 +618,7 @@ class HistoryInfo extends React.Component<{}, {}> {
|
||||
<span className="term-bright-white">[this remote ⌘R]</span>
|
||||
{" "}
|
||||
<span className="term-bright-white">[including metacmds ⌘M]</span>
|
||||
{" "} <span className="history-clickable-opt" onClick={this.handleClose}>(close ESC)</span>
|
||||
</div>
|
||||
<div className="history-items">
|
||||
<If condition={hitems.length == 0}>
|
||||
@ -937,7 +982,7 @@ class ScreenTabs extends React.Component<{session : Session}, {}> {
|
||||
@boundMethod
|
||||
handleNewScreen() {
|
||||
let {session} = this.props;
|
||||
GlobalInput.createNewScreen();
|
||||
GlobalCommandRunner.createNewScreen();
|
||||
}
|
||||
|
||||
@boundMethod
|
||||
@ -953,7 +998,7 @@ class ScreenTabs extends React.Component<{session : Session}, {}> {
|
||||
if (screen == null) {
|
||||
return;
|
||||
}
|
||||
GlobalInput.switchScreen(screenId);
|
||||
GlobalCommandRunner.switchScreen(screenId);
|
||||
}
|
||||
|
||||
handleContextMenu(e : any, screenId : string) : void {
|
||||
@ -1019,11 +1064,11 @@ class MainSideBar extends React.Component<{}, {}> {
|
||||
}
|
||||
|
||||
handleSessionClick(sessionId : string) {
|
||||
GlobalInput.switchSession(sessionId);
|
||||
GlobalCommandRunner.switchSession(sessionId);
|
||||
}
|
||||
|
||||
handleNewSession() {
|
||||
GlobalInput.createNewSession();
|
||||
GlobalCommandRunner.createNewSession();
|
||||
}
|
||||
|
||||
clickRemotes() {
|
||||
|
178
src/model.ts
178
src/model.ts
@ -51,6 +51,7 @@ type ElectronApi = {
|
||||
getId : () => string,
|
||||
onTCmd : (callback : (mods : KeyModsType) => void) => void,
|
||||
onICmd : (callback : (mods : KeyModsType) => void) => void,
|
||||
onHCmd : (callback : (mods : KeyModsType) => void) => void,
|
||||
onMetaArrowUp : (callback : () => void) => void,
|
||||
onMetaArrowDown : (callback : () => void) => void,
|
||||
onBracketCmd : (callback : (event : any, arg : {relative : number}, mods : KeyModsType) => void) => void,
|
||||
@ -573,10 +574,10 @@ type HistoryQueryOpts = {
|
||||
class InputModel {
|
||||
historyShow : OV<boolean> = mobx.observable.box(false);
|
||||
infoShow : OV<boolean> = mobx.observable.box(false);
|
||||
|
||||
|
||||
loadId : string = null;
|
||||
historyLoading : mobx.IObservableValue<boolean> = mobx.observable.box(false);
|
||||
historySessionId : string = null;
|
||||
historyItems : mobx.IObservableValue<HistoryItem[]> = mobx.observable.box(null, {name: "history-items", deep: false});
|
||||
historyItems : mobx.IObservableValue<HistoryItem[]> = mobx.observable.box(null, {name: "history-items", deep: false}); // sorted in reverse (most recent is index 0)
|
||||
historyIndex : mobx.IObservableValue<number> = mobx.observable.box(0, {name: "history-index"}); // 1-indexed (because 0 is current)
|
||||
modHistory : mobx.IObservableArray<string> = mobx.observable.array([""], {name: "mod-history"});
|
||||
setHIdx : number = 0;
|
||||
@ -586,6 +587,13 @@ class InputModel {
|
||||
infoTimeoutId : any = null;
|
||||
historySelectedNum : OV<string> = mobx.observable.box(null);
|
||||
|
||||
focusCmdInput() : void {
|
||||
let elem = document.getElementById("main-cmd-input");
|
||||
if (elem != null) {
|
||||
elem.focus();
|
||||
}
|
||||
}
|
||||
|
||||
updateCmdLine(cmdLine : CmdLineUpdateType) : void {
|
||||
mobx.action(() => {
|
||||
let curLine = this.getCurLine();
|
||||
@ -615,6 +623,103 @@ class InputModel {
|
||||
})();
|
||||
}
|
||||
|
||||
getFilteredHistoryItems() : HistoryItem[] {
|
||||
let hitems : HistoryItem[] = this.historyItems.get() ?? [];
|
||||
return hitems;
|
||||
}
|
||||
|
||||
findCurrentHistoryIndex() : number {
|
||||
let hitems = this.historyItems.get();
|
||||
let selNum = this.historySelectedNum.get();
|
||||
for (let i=0; i<hitems.length; i++) {
|
||||
if (hitems[i].historynum == selNum) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
scrollHistoryItemIntoView(hnum : string) : void {
|
||||
let elem : HTMLElement = document.querySelector(".cmd-history .hnum-" + hnum);
|
||||
if (elem == null) {
|
||||
return;
|
||||
}
|
||||
let historyDiv = elem.closest(".cmd-history");
|
||||
if (historyDiv == null) {
|
||||
return;
|
||||
}
|
||||
let buffer = 15;
|
||||
let titleHeight = 24;
|
||||
let elemOffset = elem.offsetTop;
|
||||
let elemHeight = elem.clientHeight;
|
||||
let topPos = historyDiv.scrollTop;
|
||||
let endPos = topPos + historyDiv.clientHeight;
|
||||
if (elemOffset + elemHeight + buffer > endPos) {
|
||||
if (elemHeight + buffer > historyDiv.clientHeight - titleHeight) {
|
||||
historyDiv.scrollTop = elemOffset - titleHeight;
|
||||
return;
|
||||
}
|
||||
historyDiv.scrollTop = elemOffset - historyDiv.clientHeight + elemHeight + buffer;
|
||||
return;
|
||||
}
|
||||
if (elemOffset < topPos + titleHeight) {
|
||||
if (elemHeight + buffer > historyDiv.clientHeight - titleHeight) {
|
||||
historyDiv.scrollTop = elemOffset - titleHeight;
|
||||
return;
|
||||
}
|
||||
historyDiv.scrollTop = elemOffset - titleHeight - buffer;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
setHistorySelectionNum(hnum : string) : void {
|
||||
mobx.action(() => {
|
||||
this.historySelectedNum.set(hnum);
|
||||
this.scrollHistoryItemIntoView(hnum);
|
||||
})();
|
||||
}
|
||||
|
||||
grabSelectedHistoryItem() : void {
|
||||
let idx = this.findCurrentHistoryIndex();
|
||||
if (idx == -1) {
|
||||
return;
|
||||
}
|
||||
let hitem = this.historyItems.get()[idx];
|
||||
mobx.action(() => {
|
||||
this.clearCurLine();
|
||||
this.setCurLine(hitem.cmdstr);
|
||||
this.historyShow.set(false);
|
||||
})();
|
||||
}
|
||||
|
||||
moveHistorySelection(amt : number) : void {
|
||||
let hitems : HistoryItem[] = this.historyItems.get() ?? [];
|
||||
if (hitems.length == 0 || amt == 0) {
|
||||
return;
|
||||
}
|
||||
let idx = this.findCurrentHistoryIndex();
|
||||
if (idx == -1) {
|
||||
if (amt < 0) {
|
||||
let hitem = hitems[hitems.length-1];
|
||||
this.setHistorySelectionNum(hitem.historynum);
|
||||
}
|
||||
else {
|
||||
let hitem = hitems[0];
|
||||
this.setHistorySelectionNum(hitem.historynum);
|
||||
}
|
||||
return;
|
||||
}
|
||||
idx += -amt; // negate because history array is sorted in reverse
|
||||
if (idx < 0) {
|
||||
idx = 0;
|
||||
}
|
||||
if (idx >= hitems.length) {
|
||||
idx = hitems.length-1;
|
||||
}
|
||||
let hitem = hitems[idx];
|
||||
this.setHistorySelectionNum(hitem.historynum);
|
||||
}
|
||||
|
||||
flashInfoMsg(info : InfoType, timeoutMs : number) : void {
|
||||
if (this.infoTimeoutId != null) {
|
||||
clearTimeout(this.infoTimeoutId);
|
||||
@ -655,17 +760,6 @@ class InputModel {
|
||||
return div.scrollHeight > div.clientHeight;
|
||||
}
|
||||
|
||||
hasScrollingHistory() : boolean {
|
||||
if (!this.historyShow.get()) {
|
||||
return false;
|
||||
}
|
||||
let div = document.querySelector(".cmd-history");
|
||||
if (div == null) {
|
||||
return false;
|
||||
}
|
||||
return div.scrollHeight > div.clientHeight;
|
||||
}
|
||||
|
||||
clearInfoMsg(setNull : boolean) : void {
|
||||
this.infoTimeoutId = null;
|
||||
mobx.action(() => {
|
||||
@ -732,14 +826,18 @@ class InputModel {
|
||||
this.setHIdx = 0;
|
||||
return;
|
||||
}
|
||||
let loadId = uuidv4();
|
||||
mobx.action(() => {
|
||||
this.historySessionId = sessionId;
|
||||
this.loadId = loadId;
|
||||
this.historyItems.set(null);
|
||||
this.historyLoading.set(true);
|
||||
})();
|
||||
let usp = new URLSearchParams({sessionid: sessionId, windowid: win.windowId});
|
||||
let url = new URL("http://localhost:8080/api/get-history?" + usp.toString());
|
||||
fetch(url).then((resp) => handleJsonFetchResponse(url, resp)).then((data) => {
|
||||
if (loadId != this.loadId) {
|
||||
return; // stale load
|
||||
}
|
||||
mobx.action(() => {
|
||||
if (!this.historyLoading.get()) {
|
||||
return;
|
||||
@ -750,7 +848,6 @@ class InputModel {
|
||||
}
|
||||
if (data.data && data.data.history) {
|
||||
let hitems : HistoryItem[] = data.data.history || [];
|
||||
this.historySessionId = sessionId;
|
||||
this.historyItems.set(hitems);
|
||||
this.historyLoading.set(false);
|
||||
let hlen = hitems.length;
|
||||
@ -763,6 +860,10 @@ class InputModel {
|
||||
}
|
||||
})();
|
||||
}).catch((err) => {
|
||||
let isStale = (loadId != this.loadId);
|
||||
if (isStale) {
|
||||
return;
|
||||
}
|
||||
GlobalModel.errorHandler("getting history items", err, false);
|
||||
mobx.action(() => {
|
||||
this.historyLoading.set(false);
|
||||
@ -873,6 +974,7 @@ class Model {
|
||||
this.inputModel = new InputModel();
|
||||
getApi().onTCmd(this.onTCmd.bind(this));
|
||||
getApi().onICmd(this.onICmd.bind(this));
|
||||
getApi().onHCmd(this.onHCmd.bind(this));
|
||||
getApi().onMetaArrowUp(this.onMetaArrowUp.bind(this));
|
||||
getApi().onMetaArrowDown(this.onMetaArrowDown.bind(this));
|
||||
getApi().onBracketCmd(this.onBracketCmd.bind(this));
|
||||
@ -917,22 +1019,22 @@ class Model {
|
||||
return rtn;
|
||||
}
|
||||
|
||||
onTCmd(mods : KeyModsType) {
|
||||
onTCmd(e : any, mods : KeyModsType) {
|
||||
console.log("got cmd-t", mods);
|
||||
GlobalInput.createNewScreen();
|
||||
GlobalCommandRunner.createNewScreen();
|
||||
}
|
||||
|
||||
focusCmdInput() : void {
|
||||
let elem = document.getElementById("main-cmd-input");
|
||||
if (elem != null) {
|
||||
elem.focus();
|
||||
onICmd(e : any, mods : KeyModsType) {
|
||||
this.inputModel.focusCmdInput();
|
||||
}
|
||||
|
||||
onHCmd(e : any, mods : KeyModsType) {
|
||||
let focusedLine = this.getFocusedLine();
|
||||
if (focusedLine != null && focusedLine.cmdInputFocus) {
|
||||
GlobalCommandRunner.openHistory();
|
||||
}
|
||||
}
|
||||
|
||||
onICmd(mods : KeyModsType) {
|
||||
this.focusCmdInput();
|
||||
}
|
||||
|
||||
getFocusedLine() : LineFocusType {
|
||||
let elem = document.getElementById("main-cmd-input");
|
||||
if (document.activeElement == elem) {
|
||||
@ -1019,7 +1121,7 @@ class Model {
|
||||
}
|
||||
let runningLines = win.getRunningCmdLines();
|
||||
if (runningLines.length == 0) {
|
||||
this.focusCmdInput();
|
||||
this.inputModel.focusCmdInput();
|
||||
return;
|
||||
}
|
||||
let foundIdx = -1;
|
||||
@ -1030,7 +1132,7 @@ class Model {
|
||||
}
|
||||
}
|
||||
if (foundIdx == -1 || foundIdx == runningLines.length - 1) {
|
||||
this.focusCmdInput();
|
||||
this.inputModel.focusCmdInput();
|
||||
return;
|
||||
}
|
||||
let switchLine = runningLines[foundIdx+1];
|
||||
@ -1048,15 +1150,15 @@ class Model {
|
||||
|
||||
onBracketCmd(e : any, arg : {relative: number}, mods : KeyModsType) {
|
||||
if (arg.relative == 1) {
|
||||
GlobalInput.switchScreen("+");
|
||||
GlobalCommandRunner.switchScreen("+");
|
||||
}
|
||||
else if (arg.relative == -1) {
|
||||
GlobalInput.switchScreen("-");
|
||||
GlobalCommandRunner.switchScreen("-");
|
||||
}
|
||||
}
|
||||
|
||||
onDigitCmd(e : any, arg : {digit: number}, mods : KeyModsType) {
|
||||
GlobalInput.switchScreen(String(arg.digit));
|
||||
GlobalCommandRunner.switchScreen(String(arg.digit));
|
||||
}
|
||||
|
||||
isConnected() : boolean {
|
||||
@ -1439,7 +1541,7 @@ class Model {
|
||||
}
|
||||
}
|
||||
|
||||
class InputClass {
|
||||
class CommandRunner {
|
||||
constructor() {
|
||||
}
|
||||
|
||||
@ -1450,6 +1552,10 @@ class InputClass {
|
||||
})();
|
||||
}
|
||||
|
||||
openHistory() {
|
||||
GlobalModel.submitCommand("history", null, null, {"nohist": "1"}, true);
|
||||
}
|
||||
|
||||
switchSession(session : string) {
|
||||
GlobalModel.submitCommand("session", null, [session], {"nohist": "1"}, false);
|
||||
this.clearCmdInput();
|
||||
@ -1477,14 +1583,14 @@ class InputClass {
|
||||
};
|
||||
|
||||
let GlobalModel : Model = null;
|
||||
let GlobalInput : InputClass = null;
|
||||
let GlobalCommandRunner : CommandRunner = null;
|
||||
if ((window as any).GlobalModal == null) {
|
||||
(window as any).GlobalModel = new Model();
|
||||
(window as any).GlobalInput = new InputClass();
|
||||
(window as any).GlobalCommandRunner = new CommandRunner();
|
||||
}
|
||||
GlobalModel = (window as any).GlobalModel;
|
||||
GlobalInput = (window as any).GlobalInput;
|
||||
GlobalCommandRunner = (window as any).GlobalCommandRunner;
|
||||
|
||||
export {Model, Session, Window, GlobalModel, GlobalInput, Cmd, Screen, ScreenWindow, riToRPtr};
|
||||
export {Model, Session, Window, GlobalModel, GlobalCommandRunner, Cmd, Screen, ScreenWindow, riToRPtr};
|
||||
|
||||
|
||||
|
@ -4,6 +4,7 @@ contextBridge.exposeInMainWorld("api", {
|
||||
getId: () => ipcRenderer.sendSync("get-id"),
|
||||
onTCmd: (callback) => ipcRenderer.on("t-cmd", callback),
|
||||
onICmd: (callback) => ipcRenderer.on("i-cmd", callback),
|
||||
onHCmd: (callback) => ipcRenderer.on("h-cmd", callback),
|
||||
onMetaArrowUp: (callback) => ipcRenderer.on("meta-arrowup", callback),
|
||||
onMetaArrowDown: (callback) => ipcRenderer.on("meta-arrowdown", callback),
|
||||
onBracketCmd: (callback) => ipcRenderer.on("bracket-cmd", callback),
|
||||
|
@ -617,11 +617,12 @@ body .xterm .xterm-viewport {
|
||||
.cmd-history {
|
||||
color: @term-white;
|
||||
margin-bottom: 5px;
|
||||
overflow-y: auto;
|
||||
overflow: auto;
|
||||
flex-shrink: 1;
|
||||
|
||||
.history-title {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
top: 5px;
|
||||
left: 20px;
|
||||
background-color: black;
|
||||
@ -630,6 +631,10 @@ body .xterm .xterm-viewport {
|
||||
font-size: 14px;
|
||||
color: #729fcf;
|
||||
padding-bottom: 4px;
|
||||
|
||||
.history-clickable-opt {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.history-items {
|
||||
|
@ -129,6 +129,7 @@ type HistoryItem = {
|
||||
remove : boolean,
|
||||
remote : RemotePtrType,
|
||||
ismetacmd : boolean,
|
||||
historynum : string,
|
||||
};
|
||||
|
||||
type CmdRemoteStateType = {
|
||||
|
Loading…
Reference in New Issue
Block a user