mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-01-02 18:39:05 +01:00
global shortcut for wave (#287)
* working on easy global shortcut for wave * globalshortcut setting working * cmd for macos, alt for others * re-remove types.ts (was added back during merge) * rename DDItem to DropdownItem, put into custom.d.ts * make some consts
This commit is contained in:
parent
18fe3f3296
commit
3e4bd458b3
@ -29,12 +29,12 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
|||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
handleChangeFontSize(fontSize: string): void {
|
handleChangeFontSize(fontSize: string): void {
|
||||||
let newFontSize = Number(fontSize);
|
const newFontSize = Number(fontSize);
|
||||||
this.fontSizeDropdownActive.set(false);
|
this.fontSizeDropdownActive.set(false);
|
||||||
if (GlobalModel.termFontSize.get() == newFontSize) {
|
if (GlobalModel.termFontSize.get() == newFontSize) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let prtn = GlobalCommandRunner.setTermFontSize(newFontSize, false);
|
const prtn = GlobalCommandRunner.setTermFontSize(newFontSize, false);
|
||||||
commandRtnHandler(prtn, this.errorMessage);
|
commandRtnHandler(prtn, this.errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,29 +67,29 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
|||||||
commandRtnHandler(prtn, this.errorMessage);
|
commandRtnHandler(prtn, this.errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
getFontSizes(): any {
|
getFontSizes(): DropdownItem[] {
|
||||||
let availableFontSizes: { label: string; value: number }[] = [];
|
const availableFontSizes: DropdownItem[] = [];
|
||||||
for (let s = appconst.MinFontSize; s <= appconst.MaxFontSize; s++) {
|
for (let s = appconst.MinFontSize; s <= appconst.MaxFontSize; s++) {
|
||||||
availableFontSizes.push({ label: s + "px", value: s });
|
availableFontSizes.push({ label: s + "px", value: String(s) });
|
||||||
}
|
}
|
||||||
return availableFontSizes;
|
return availableFontSizes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
inlineUpdateOpenAIModel(newModel: string): void {
|
inlineUpdateOpenAIModel(newModel: string): void {
|
||||||
let prtn = GlobalCommandRunner.setClientOpenAISettings({ model: newModel });
|
const prtn = GlobalCommandRunner.setClientOpenAISettings({ model: newModel });
|
||||||
commandRtnHandler(prtn, this.errorMessage);
|
commandRtnHandler(prtn, this.errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
inlineUpdateOpenAIToken(newToken: string): void {
|
inlineUpdateOpenAIToken(newToken: string): void {
|
||||||
let prtn = GlobalCommandRunner.setClientOpenAISettings({ apitoken: newToken });
|
const prtn = GlobalCommandRunner.setClientOpenAISettings({ apitoken: newToken });
|
||||||
commandRtnHandler(prtn, this.errorMessage);
|
commandRtnHandler(prtn, this.errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@boundMethod
|
@boundMethod
|
||||||
inlineUpdateOpenAIMaxTokens(newMaxTokensStr: string): void {
|
inlineUpdateOpenAIMaxTokens(newMaxTokensStr: string): void {
|
||||||
let prtn = GlobalCommandRunner.setClientOpenAISettings({ maxtokens: newMaxTokensStr });
|
const prtn = GlobalCommandRunner.setClientOpenAISettings({ maxtokens: newMaxTokensStr });
|
||||||
commandRtnHandler(prtn, this.errorMessage);
|
commandRtnHandler(prtn, this.errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,19 +105,41 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
|||||||
GlobalModel.clientSettingsViewModel.closeView();
|
GlobalModel.clientSettingsViewModel.closeView();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@boundMethod
|
||||||
|
handleChangeShortcut(newShortcut: string): void {
|
||||||
|
const prtn = GlobalCommandRunner.setGlobalShortcut(newShortcut);
|
||||||
|
commandRtnHandler(prtn, this.errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFKeys(): DropdownItem[] {
|
||||||
|
const opts: DropdownItem[] = [];
|
||||||
|
opts.push({ label: "Disabled", value: "" });
|
||||||
|
const platform = GlobalModel.getPlatform();
|
||||||
|
for (let i = 1; i <= 12; i++) {
|
||||||
|
const shortcut = (platform == "darwin" ? "Cmd" : "Alt") + "+F" + String(i);
|
||||||
|
opts.push({ label: shortcut, value: shortcut });
|
||||||
|
}
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCurrentShortcut(): string {
|
||||||
|
const clientData = GlobalModel.clientData.get();
|
||||||
|
return clientData?.clientopts?.globalshortcut ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let isHidden = GlobalModel.activeMainView.get() != "clientsettings";
|
const isHidden = GlobalModel.activeMainView.get() != "clientsettings";
|
||||||
if (isHidden) {
|
if (isHidden) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cdata: ClientDataType = GlobalModel.clientData.get();
|
const cdata: ClientDataType = GlobalModel.clientData.get();
|
||||||
let openAIOpts = cdata.openaiopts ?? {};
|
const openAIOpts = cdata.openaiopts ?? {};
|
||||||
let apiTokenStr = isBlank(openAIOpts.apitoken) ? "(not set)" : "********";
|
const apiTokenStr = isBlank(openAIOpts.apitoken) ? "(not set)" : "********";
|
||||||
let maxTokensStr = String(
|
const maxTokensStr = String(
|
||||||
openAIOpts.maxtokens == null || openAIOpts.maxtokens == 0 ? 1000 : openAIOpts.maxtokens
|
openAIOpts.maxtokens == null || openAIOpts.maxtokens == 0 ? 1000 : openAIOpts.maxtokens
|
||||||
);
|
);
|
||||||
let curFontSize = GlobalModel.termFontSize.get();
|
const curFontSize = GlobalModel.termFontSize.get();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("view clientsettings-view")}>
|
<div className={cn("view clientsettings-view")}>
|
||||||
@ -207,6 +229,17 @@ class ClientSettingsView extends React.Component<{ model: RemotesModel }, { hove
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="settings-field">
|
||||||
|
<div className="settings-label">Global Hotkey</div>
|
||||||
|
<div className="settings-input">
|
||||||
|
<Dropdown
|
||||||
|
className="hotkey-dropdown"
|
||||||
|
options={this.getFKeys()}
|
||||||
|
defaultValue={this.getCurrentShortcut()}
|
||||||
|
onChange={this.handleChangeShortcut}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<SettingsError errorMessage={this.errorMessage} />
|
<SettingsError errorMessage={this.errorMessage} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -17,7 +17,7 @@ interface DropdownDecorationProps {
|
|||||||
|
|
||||||
interface DropdownProps {
|
interface DropdownProps {
|
||||||
label?: string;
|
label?: string;
|
||||||
options: { value: string; label: string }[];
|
options: DropdownItem[];
|
||||||
value?: string;
|
value?: string;
|
||||||
className?: string;
|
className?: string;
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
|
@ -8,8 +8,9 @@ import fetch from "node-fetch";
|
|||||||
import * as child_process from "node:child_process";
|
import * as child_process from "node:child_process";
|
||||||
import { debounce } from "throttle-debounce";
|
import { debounce } from "throttle-debounce";
|
||||||
import * as winston from "winston";
|
import * as winston from "winston";
|
||||||
import { sprintf } from "sprintf-js";
|
|
||||||
import * as util from "util";
|
import * as util from "util";
|
||||||
|
import * as waveutil from "../util/util";
|
||||||
|
import { sprintf } from "sprintf-js";
|
||||||
import { handleJsonFetchResponse } from "@/util/util";
|
import { handleJsonFetchResponse } from "@/util/util";
|
||||||
import { v4 as uuidv4 } from "uuid";
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { checkKeyPressed, adaptFromElectronKeyEvent, setKeyUtilPlatform } from "@/util/keyutil";
|
import { checkKeyPressed, adaptFromElectronKeyEvent, setKeyUtilPlatform } from "@/util/keyutil";
|
||||||
@ -29,6 +30,7 @@ let instanceId = uuidv4();
|
|||||||
let oldConsoleLog = console.log;
|
let oldConsoleLog = console.log;
|
||||||
let wasActive = true;
|
let wasActive = true;
|
||||||
let wasInFg = true;
|
let wasInFg = true;
|
||||||
|
let currentGlobalShortcut: string | null = null;
|
||||||
|
|
||||||
checkPromptMigrate();
|
checkPromptMigrate();
|
||||||
ensureDir(waveHome);
|
ensureDir(waveHome);
|
||||||
@ -412,7 +414,7 @@ function mainResizeHandler(e, win) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcBounds(clientData) {
|
function calcBounds(clientData: ClientDataType) {
|
||||||
let primaryDisplay = electron.screen.getPrimaryDisplay();
|
let primaryDisplay = electron.screen.getPrimaryDisplay();
|
||||||
let pdBounds = primaryDisplay.bounds;
|
let pdBounds = primaryDisplay.bounds;
|
||||||
let size = { x: 100, y: 100, width: pdBounds.width - 200, height: pdBounds.height - 200 };
|
let size = { x: 100, y: 100, width: pdBounds.width - 200, height: pdBounds.height - 200 };
|
||||||
@ -509,6 +511,12 @@ electron.ipcMain.on("open-external-link", async (_, url) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
electron.ipcMain.on("reregister-global-shortcut", (event, shortcut: string) => {
|
||||||
|
reregisterGlobalShortcut(shortcut);
|
||||||
|
event.returnValue = true;
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
|
||||||
electron.ipcMain.on("get-last-logs", async (event, numberOfLines) => {
|
electron.ipcMain.on("get-last-logs", async (event, numberOfLines) => {
|
||||||
try {
|
try {
|
||||||
const logPath = path.join(getWaveHomeDir(), "wavesrv.log");
|
const logPath = path.join(getWaveHomeDir(), "wavesrv.log");
|
||||||
@ -698,6 +706,34 @@ function runActiveTimer() {
|
|||||||
setTimeout(runActiveTimer, 60000);
|
setTimeout(runActiveTimer, 60000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reregisterGlobalShortcut(shortcut: string) {
|
||||||
|
if (shortcut == "") {
|
||||||
|
shortcut = null;
|
||||||
|
}
|
||||||
|
if (currentGlobalShortcut == shortcut) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!waveutil.isBlank(currentGlobalShortcut)) {
|
||||||
|
if (electron.globalShortcut.isRegistered(currentGlobalShortcut)) {
|
||||||
|
electron.globalShortcut.unregister(currentGlobalShortcut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (waveutil.isBlank(shortcut)) {
|
||||||
|
currentGlobalShortcut = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let ok = electron.globalShortcut.register(shortcut, () => {
|
||||||
|
console.log("global shortcut triggered, showing window");
|
||||||
|
MainWindow?.show();
|
||||||
|
});
|
||||||
|
console.log("registered global shortcut", shortcut, ok ? "ok" : "failed");
|
||||||
|
if (!ok) {
|
||||||
|
currentGlobalShortcut = null;
|
||||||
|
console.log("failed to register global shortcut", shortcut);
|
||||||
|
}
|
||||||
|
currentGlobalShortcut = shortcut;
|
||||||
|
}
|
||||||
|
|
||||||
// ====== MAIN ====== //
|
// ====== MAIN ====== //
|
||||||
|
|
||||||
(async () => {
|
(async () => {
|
||||||
|
@ -12,6 +12,7 @@ contextBridge.exposeInMainWorld("api", {
|
|||||||
},
|
},
|
||||||
restartWaveSrv: () => ipcRenderer.sendSync("restart-server"),
|
restartWaveSrv: () => ipcRenderer.sendSync("restart-server"),
|
||||||
reloadWindow: () => ipcRenderer.sendSync("reload-window"),
|
reloadWindow: () => ipcRenderer.sendSync("reload-window"),
|
||||||
|
reregisterGlobalShortcut: (shortcut) => ipcRenderer.sendSync("reregister-global-shortcut", shortcut),
|
||||||
openExternalLink: (url) => ipcRenderer.send("open-external-link", url),
|
openExternalLink: (url) => ipcRenderer.send("open-external-link", url),
|
||||||
onTCmd: (callback) => ipcRenderer.on("t-cmd", callback),
|
onTCmd: (callback) => ipcRenderer.on("t-cmd", callback),
|
||||||
onICmd: (callback) => ipcRenderer.on("i-cmd", callback),
|
onICmd: (callback) => ipcRenderer.on("i-cmd", callback),
|
||||||
|
@ -431,6 +431,10 @@ class CommandRunner {
|
|||||||
}
|
}
|
||||||
GlobalModel.submitCommand("sidebar", "open", null, kwargs, false);
|
GlobalModel.submitCommand("sidebar", "open", null, kwargs, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setGlobalShortcut(shortcut: string): Promise<CommandRtnType> {
|
||||||
|
return GlobalModel.submitCommand("client", "setglobalshortcut", [shortcut], { nohist: "1" }, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { CommandRunner };
|
export { CommandRunner };
|
||||||
|
@ -55,6 +55,7 @@ type ElectronApi = {
|
|||||||
restartWaveSrv: () => boolean;
|
restartWaveSrv: () => boolean;
|
||||||
reloadWindow: () => void;
|
reloadWindow: () => void;
|
||||||
openExternalLink: (url: string) => void;
|
openExternalLink: (url: string) => void;
|
||||||
|
reregisterGlobalShortcut: (shortcut: string) => void;
|
||||||
onTCmd: (callback: (mods: KeyModsType) => void) => void;
|
onTCmd: (callback: (mods: KeyModsType) => void) => void;
|
||||||
onICmd: (callback: (mods: KeyModsType) => void) => void;
|
onICmd: (callback: (mods: KeyModsType) => void) => void;
|
||||||
onLCmd: (callback: (mods: KeyModsType) => void) => void;
|
onLCmd: (callback: (mods: KeyModsType) => void) => void;
|
||||||
@ -887,7 +888,7 @@ class Model {
|
|||||||
this.bookmarksModel.mergeBookmarks(update.bookmarks.bookmarks);
|
this.bookmarksModel.mergeBookmarks(update.bookmarks.bookmarks);
|
||||||
}
|
}
|
||||||
} else if (update.clientdata != null) {
|
} else if (update.clientdata != null) {
|
||||||
this.clientData.set(update.clientdata);
|
this.setClientData(update.clientdata);
|
||||||
} else if (update.cmdline != null) {
|
} else if (update.cmdline != null) {
|
||||||
this.inputModel.updateCmdLine(update.cmdline);
|
this.inputModel.updateCmdLine(update.cmdline);
|
||||||
} else if (update.openaicmdinfochat != null) {
|
} else if (update.openaicmdinfochat != null) {
|
||||||
@ -1095,16 +1096,25 @@ class Model {
|
|||||||
fetch(url, { method: "post", body: null, headers: fetchHeaders })
|
fetch(url, { method: "post", body: null, headers: fetchHeaders })
|
||||||
.then((resp) => handleJsonFetchResponse(url, resp))
|
.then((resp) => handleJsonFetchResponse(url, resp))
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
mobx.action(() => {
|
|
||||||
const clientData: ClientDataType = data.data;
|
const clientData: ClientDataType = data.data;
|
||||||
this.clientData.set(clientData);
|
this.setClientData(clientData);
|
||||||
})();
|
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
this.errorHandler("calling get-client-data", err, true);
|
this.errorHandler("calling get-client-data", err, true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setClientData(clientData: ClientDataType) {
|
||||||
|
mobx.action(() => {
|
||||||
|
this.clientData.set(clientData);
|
||||||
|
})();
|
||||||
|
let shortcut = null;
|
||||||
|
if (clientData?.clientopts?.globalshortcutenabled) {
|
||||||
|
shortcut = clientData?.clientopts?.globalshortcut;
|
||||||
|
}
|
||||||
|
getApi().reregisterGlobalShortcut(shortcut);
|
||||||
|
}
|
||||||
|
|
||||||
submitCommandPacket(cmdPk: FeCmdPacketType, interactive: boolean): Promise<CommandRtnType> {
|
submitCommandPacket(cmdPk: FeCmdPacketType, interactive: boolean): Promise<CommandRtnType> {
|
||||||
if (this.debugCmds > 0) {
|
if (this.debugCmds > 0) {
|
||||||
console.log("[cmd]", cmdPacketString(cmdPk));
|
console.log("[cmd]", cmdPacketString(cmdPk));
|
||||||
@ -1328,6 +1338,10 @@ class Model {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendUserInput(userInputResponsePacket: UserInputResponsePacket) {
|
||||||
|
this.ws.pushMessage(userInputResponsePacket);
|
||||||
|
}
|
||||||
|
|
||||||
sendInputPacket(inputPacket: any) {
|
sendInputPacket(inputPacket: any) {
|
||||||
this.ws.pushMessage(inputPacket);
|
this.ws.pushMessage(inputPacket);
|
||||||
}
|
}
|
||||||
|
7
src/types/custom.d.ts
vendored
7
src/types/custom.d.ts
vendored
@ -292,6 +292,11 @@ declare global {
|
|||||||
userquery?: string;
|
userquery?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type DropdownItem = {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Levels for the screen status indicator
|
* Levels for the screen status indicator
|
||||||
*/
|
*/
|
||||||
@ -554,6 +559,8 @@ declare global {
|
|||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
width: number;
|
width: number;
|
||||||
};
|
};
|
||||||
|
globalshortcut: string;
|
||||||
|
globalshortcutenabled: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ReleaseInfoType = {
|
type ReleaseInfoType = {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"include": ["src/**/*", "types/**/*"],
|
"include": ["src/**/*", "types/**/*"],
|
||||||
// "exclude": ["src/electron/emain.ts"],
|
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
|
@ -224,6 +224,7 @@ func init() {
|
|||||||
registerCmdFn("client:accepttos", ClientAcceptTosCommand)
|
registerCmdFn("client:accepttos", ClientAcceptTosCommand)
|
||||||
registerCmdFn("client:setconfirmflag", ClientConfirmFlagCommand)
|
registerCmdFn("client:setconfirmflag", ClientConfirmFlagCommand)
|
||||||
registerCmdFn("client:setsidebar", ClientSetSidebarCommand)
|
registerCmdFn("client:setsidebar", ClientSetSidebarCommand)
|
||||||
|
registerCmdFn("client:setglobalshortcut", ClientSetGlobalShortcut)
|
||||||
|
|
||||||
registerCmdFn("sidebar:open", SidebarOpenCommand)
|
registerCmdFn("sidebar:open", SidebarOpenCommand)
|
||||||
registerCmdFn("sidebar:close", SidebarCloseCommand)
|
registerCmdFn("sidebar:close", SidebarCloseCommand)
|
||||||
@ -4990,6 +4991,28 @@ func ClientConfirmFlagCommand(ctx context.Context, pk *scpacket.FeCommandPacketT
|
|||||||
return update, nil
|
return update, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ClientSetGlobalShortcut(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
||||||
|
clientData, err := sstore.EnsureClientData(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("cannot retrieve client data: %v", err)
|
||||||
|
}
|
||||||
|
newShortcut := firstArg(pk)
|
||||||
|
if len(newShortcut) > 50 {
|
||||||
|
return nil, fmt.Errorf("invalid shortcut (maxlen = 50)")
|
||||||
|
}
|
||||||
|
clientOpts := clientData.ClientOpts
|
||||||
|
clientOpts.GlobalShortcut = newShortcut
|
||||||
|
clientOpts.GlobalShortcutEnabled = (newShortcut != "")
|
||||||
|
err = sstore.SetClientOpts(ctx, clientOpts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error updating client data: %v", err)
|
||||||
|
}
|
||||||
|
clientData.ClientOpts = clientOpts
|
||||||
|
update := &sstore.ModelUpdate{}
|
||||||
|
sstore.AddUpdate(update, *clientData)
|
||||||
|
return update, nil
|
||||||
|
}
|
||||||
|
|
||||||
func ClientSetSidebarCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
func ClientSetSidebarCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
|
||||||
clientData, err := sstore.EnsureClientData(ctx)
|
clientData, err := sstore.EnsureClientData(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -288,6 +288,8 @@ type ClientOptsType struct {
|
|||||||
AcceptedTos int64 `json:"acceptedtos,omitempty"`
|
AcceptedTos int64 `json:"acceptedtos,omitempty"`
|
||||||
ConfirmFlags map[string]bool `json:"confirmflags,omitempty"`
|
ConfirmFlags map[string]bool `json:"confirmflags,omitempty"`
|
||||||
MainSidebar *SidebarValueType `json:"mainsidebar,omitempty"`
|
MainSidebar *SidebarValueType `json:"mainsidebar,omitempty"`
|
||||||
|
GlobalShortcut string `json:"globalshortcut,omitempty"`
|
||||||
|
GlobalShortcutEnabled bool `json:"globalshortcutenabled,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type FeOptsType struct {
|
type FeOptsType struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user