@vespura/vmenu-plugin 1.0.14 → 1.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/client.js +200 -12
- package/dist/server.js +2 -1
- package/dist/types/client/host.d.ts +2 -1
- package/dist/types/client/index.d.ts +1 -1
- package/dist/types/client/menu.d.ts +66 -2
- package/dist/types/client/plugin.d.ts +20 -0
- package/dist/types/protocol.d.ts +34 -1
- package/dist/vmenu.js +201 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ The TypeScript and JavaScript plugin API for vMenu Enhanced, the version of vMen
|
|
|
4
4
|
|
|
5
5
|
A plugin is a normal FiveM resource of your own. With this package, your resource can add menus to vMenu, under a Plugins entry on its main menu. You describe the menu in code and the package talks to vMenu over events for you. vMenu never needs to know about your resource ahead of time.
|
|
6
6
|
|
|
7
|
-
Everything the C# plugin API can do is here too. That covers buttons, checkboxes, lists, sliders, dynamic lists, submenus, confirm rows, separators, keys, player actions, translations, permission and setting gates, notifications, text prompts and
|
|
7
|
+
Everything the C# plugin API can do is here too. That covers buttons, checkboxes, lists, sliders, dynamic lists, submenus, confirm rows, separators, keys, player actions, translations, permission and setting gates, notifications, text prompts, themes and standalone menus.
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
10
|
import { Gate, NotifyStyle, Text, VMenuPlugin } from '@vespura/vmenu-plugin/client';
|
package/dist/client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/version.ts
|
|
2
|
-
var version = true ? "1.0.
|
|
2
|
+
var version = true ? "1.0.15" : "0.0.0-local";
|
|
3
3
|
|
|
4
4
|
// src/text.ts
|
|
5
5
|
var Text = class _Text {
|
|
@@ -133,7 +133,9 @@ function int(value) {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
// src/protocol.ts
|
|
136
|
-
var PROTOCOL_VERSION =
|
|
136
|
+
var PROTOCOL_VERSION = 3;
|
|
137
|
+
var BASE_PROTOCOL_VERSION = 2;
|
|
138
|
+
var MAX_STANDALONE_MENUS = 50;
|
|
137
139
|
var VMENU_RESOURCE = "vMenu.Enhanced";
|
|
138
140
|
var PluginEvents = {
|
|
139
141
|
Probe: "vMenu.Enhanced:Plugins:Probe",
|
|
@@ -153,6 +155,7 @@ var PluginEvents = {
|
|
|
153
155
|
registerResultFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:RegisterResult`,
|
|
154
156
|
eventFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:Event`,
|
|
155
157
|
promptResultFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:PromptResult`,
|
|
158
|
+
openResultFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:OpenResult`,
|
|
156
159
|
themesFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:Themes`,
|
|
157
160
|
themesRegisteredFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:ThemesRegistered`,
|
|
158
161
|
serverReadyFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:Server:Ready`,
|
|
@@ -173,6 +176,8 @@ var NodeEvents = {
|
|
|
173
176
|
Opened: "opened",
|
|
174
177
|
Closed: "closed",
|
|
175
178
|
IndexChanged: "indexChanged",
|
|
179
|
+
Replaced: "replaced",
|
|
180
|
+
ForceClosed: "forceClosed",
|
|
176
181
|
Highlighted: "highlighted"
|
|
177
182
|
};
|
|
178
183
|
var SettingTypes = {
|
|
@@ -195,6 +200,8 @@ var CallbackTypes = {
|
|
|
195
200
|
MenuOpened: "menuOpened",
|
|
196
201
|
MenuClosed: "menuClosed",
|
|
197
202
|
MenuIndexChanged: "menuIndexChanged",
|
|
203
|
+
MenuReplaced: "menuReplaced",
|
|
204
|
+
MenuForceClosed: "menuForceClosed",
|
|
198
205
|
PlayerActionSelected: "playerActionSelected",
|
|
199
206
|
PlayerActionConfirmed: "playerActionConfirmed",
|
|
200
207
|
PlayerActionListSelected: "playerActionListSelected",
|
|
@@ -231,6 +238,7 @@ var UpdateOps = {
|
|
|
231
238
|
SetMenuEvents: "setMenuEvents",
|
|
232
239
|
SetFilter: "setFilter",
|
|
233
240
|
ClearFilter: "clearFilter",
|
|
241
|
+
AddStandaloneMenu: "addStandaloneMenu",
|
|
234
242
|
AddKeys: "addKeys",
|
|
235
243
|
SetKeyText: "setKeyText",
|
|
236
244
|
SetKeyEnabled: "setKeyEnabled",
|
|
@@ -913,6 +921,37 @@ var PluginKey = class {
|
|
|
913
921
|
};
|
|
914
922
|
|
|
915
923
|
// src/client/menu.ts
|
|
924
|
+
var MenuPriority = {
|
|
925
|
+
/** Only opens when no vMenu or plugin menu is open. */
|
|
926
|
+
None: "none",
|
|
927
|
+
/** Replaces vMenu's menus and other menus at low priority. */
|
|
928
|
+
Low: "low",
|
|
929
|
+
/** Replaces anything at low or medium priority. */
|
|
930
|
+
Medium: "medium",
|
|
931
|
+
/**
|
|
932
|
+
* Replaces anything that is not locked. Only use this in extreme situations, prevents vMenu or other plugins with a
|
|
933
|
+
* lower priority from opening any menus while yours is open. Use responsibly!
|
|
934
|
+
*/
|
|
935
|
+
High: "high"
|
|
936
|
+
};
|
|
937
|
+
var OpenMenuResult = {
|
|
938
|
+
/** The menu is on screen. */
|
|
939
|
+
Opened: "opened",
|
|
940
|
+
/** A menu with a higher priority is open, or any menu is open for a request at `None`. */
|
|
941
|
+
BlockedByPriority: "blockedByPriority",
|
|
942
|
+
/** Another menu asked not to be closed. */
|
|
943
|
+
BlockedByLock: "blockedByLock",
|
|
944
|
+
/** vMenu does not know this menu, for example an empty root menu, which has no row yet. */
|
|
945
|
+
UnknownMenu: "unknownMenu",
|
|
946
|
+
/** The player is not allowed into this menu, for example because a gate on the row leading to it fails. */
|
|
947
|
+
NotAllowed: "notAllowed",
|
|
948
|
+
/** A later open or close in the same batch took its place. */
|
|
949
|
+
Superseded: "superseded",
|
|
950
|
+
/** The plugin is not connected to vMenu, or vMenu stopped before it answered. */
|
|
951
|
+
NotConnected: "notConnected",
|
|
952
|
+
/** The vMenu this plugin talks to predates open priorities. Nothing was opened, use `open()` there instead. */
|
|
953
|
+
NotSupported: "notSupported"
|
|
954
|
+
};
|
|
916
955
|
var PluginMenu = class _PluginMenu {
|
|
917
956
|
/** @internal */
|
|
918
957
|
constructor(plugin, node, title, subtitle) {
|
|
@@ -925,6 +964,8 @@ var PluginMenu = class _PluginMenu {
|
|
|
925
964
|
this.opened = new Signal(() => plugin.resource, "opened");
|
|
926
965
|
this.closed = new Signal(() => plugin.resource, "closed");
|
|
927
966
|
this.indexChanged = new Signal(() => plugin.resource, "indexChanged");
|
|
967
|
+
this.replaced = new Signal(() => plugin.resource, "replaced");
|
|
968
|
+
this.forceClosed = new Signal(() => plugin.resource, "forceClosed");
|
|
928
969
|
}
|
|
929
970
|
/** The menu's id, how vMenu refers to it. */
|
|
930
971
|
get id() {
|
|
@@ -1152,10 +1193,53 @@ var PluginMenu = class _PluginMenu {
|
|
|
1152
1193
|
this.node.items.length = 0;
|
|
1153
1194
|
this.plugin.emitOp({ op: UpdateOps.ClearMenu, menuId: this.id });
|
|
1154
1195
|
}
|
|
1155
|
-
/** Opens this menu on screen, closing whatever vMenu menu was open. */
|
|
1196
|
+
/** Opens this menu on screen at low priority, closing whatever vMenu menu was open. Use `requestOpen` to learn whether it worked. */
|
|
1156
1197
|
open() {
|
|
1157
1198
|
this.plugin.emitOp({ op: UpdateOps.OpenMenu, menuId: this.id });
|
|
1158
1199
|
}
|
|
1200
|
+
/**
|
|
1201
|
+
* Opens this menu only when no vMenu or plugin menu is open, so it never interrupts the player. Switching between your
|
|
1202
|
+
* own menus always works.
|
|
1203
|
+
*/
|
|
1204
|
+
requestOpen() {
|
|
1205
|
+
return this.requestOpenWithPriority(MenuPriority.None);
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Opens this menu, closing an open menu of equal or lower priority. A menu with a higher priority, or one that asked
|
|
1209
|
+
* not to be closed, keeps the screen and the request is refused.
|
|
1210
|
+
*
|
|
1211
|
+
* @param priority `High`: only use this in extreme situations, prevents vMenu or other plugins with a lower priority
|
|
1212
|
+
* from opening any menus while yours is open. Use responsibly!
|
|
1213
|
+
* @param doNotClose Only with `High`. Only use this in extreme situations. It blocks the player from using anything
|
|
1214
|
+
* else, so always close the menu as soon as possible. The lock ends when you close the menu or open its parent, and
|
|
1215
|
+
* the player can still force it shut by holding back.
|
|
1216
|
+
* @throws When `doNotClose` is set below `High`.
|
|
1217
|
+
*/
|
|
1218
|
+
requestOpenWithPriority(priority, doNotClose = false) {
|
|
1219
|
+
if (!Object.values(MenuPriority).includes(priority)) {
|
|
1220
|
+
throw new Error(`'${String(priority)}' is not a menu priority.`);
|
|
1221
|
+
}
|
|
1222
|
+
if (doNotClose && priority !== MenuPriority.High) {
|
|
1223
|
+
throw new Error("doNotClose only works with MenuPriority.High.");
|
|
1224
|
+
}
|
|
1225
|
+
return this.plugin.requestOpen(this, priority, doNotClose);
|
|
1226
|
+
}
|
|
1227
|
+
/** Called when another plugin's menu took the screen while the player was in this one. `onClosed` fires as well. */
|
|
1228
|
+
onReplaced(handler) {
|
|
1229
|
+
const unsubscribe = this.replaced.add(handler);
|
|
1230
|
+
this.subscribeMenuEvent(NodeEvents.Replaced);
|
|
1231
|
+
return unsubscribe;
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* Called when this menu was opened with `doNotClose` and closed by anything other than your own code: the player
|
|
1235
|
+
* holding back to force it shut, or vMenu having to close it. Whatever the player was in the middle of was left
|
|
1236
|
+
* unfinished.
|
|
1237
|
+
*/
|
|
1238
|
+
onForceClosed(handler) {
|
|
1239
|
+
const unsubscribe = this.forceClosed.add(handler);
|
|
1240
|
+
this.subscribeMenuEvent(NodeEvents.ForceClosed);
|
|
1241
|
+
return unsubscribe;
|
|
1242
|
+
}
|
|
1159
1243
|
/** Closes this plugin's menu if one is open. */
|
|
1160
1244
|
close() {
|
|
1161
1245
|
this.plugin.emitOp({ op: UpdateOps.CloseMenu, menuId: this.id });
|
|
@@ -1199,6 +1283,12 @@ var PluginMenu = class _PluginMenu {
|
|
|
1199
1283
|
this.indexChanged.fire(callback.oldIndex ?? 0, callback.newIndex);
|
|
1200
1284
|
}
|
|
1201
1285
|
break;
|
|
1286
|
+
case CallbackTypes.MenuReplaced:
|
|
1287
|
+
this.replaced.fire();
|
|
1288
|
+
break;
|
|
1289
|
+
case CallbackTypes.MenuForceClosed:
|
|
1290
|
+
this.forceClosed.fire();
|
|
1291
|
+
break;
|
|
1202
1292
|
}
|
|
1203
1293
|
}
|
|
1204
1294
|
/** @internal */
|
|
@@ -1206,8 +1296,12 @@ var PluginMenu = class _PluginMenu {
|
|
|
1206
1296
|
if (!node.menu) {
|
|
1207
1297
|
return node;
|
|
1208
1298
|
}
|
|
1209
|
-
|
|
1210
|
-
|
|
1299
|
+
return { ...node, menu: _PluginMenu.withoutItems(node.menu) };
|
|
1300
|
+
}
|
|
1301
|
+
/** @internal */
|
|
1302
|
+
static withoutItems(node) {
|
|
1303
|
+
const { items: _items, ...menu } = node;
|
|
1304
|
+
return { ...menu, items: [] };
|
|
1211
1305
|
}
|
|
1212
1306
|
newNode(type, text, options) {
|
|
1213
1307
|
return { id: options?.id ?? this.plugin.nextItemId(), type, text: Text.toRef(text) };
|
|
@@ -1537,6 +1631,8 @@ var VMenuPlugin = class _VMenuPlugin {
|
|
|
1537
1631
|
this.menusById = /* @__PURE__ */ new Map();
|
|
1538
1632
|
this.keysById = /* @__PURE__ */ new Map();
|
|
1539
1633
|
this.pendingPrompts = /* @__PURE__ */ new Map();
|
|
1634
|
+
this.pendingOpens = /* @__PURE__ */ new Map();
|
|
1635
|
+
this.standalone = [];
|
|
1540
1636
|
this.pendingItems = /* @__PURE__ */ new Set();
|
|
1541
1637
|
this.pendingMenus = /* @__PURE__ */ new Set();
|
|
1542
1638
|
this.dirtyFilters = [];
|
|
@@ -1544,6 +1640,8 @@ var VMenuPlugin = class _VMenuPlugin {
|
|
|
1544
1640
|
this.nextItem = 0;
|
|
1545
1641
|
this.nextMenu = 0;
|
|
1546
1642
|
this.nextPrompt = 0;
|
|
1643
|
+
this.nextOpen = 0;
|
|
1644
|
+
this.vMenuProtocol = 0;
|
|
1547
1645
|
this.handlersRegistered = false;
|
|
1548
1646
|
this.connected = false;
|
|
1549
1647
|
this.resource = currentResource();
|
|
@@ -1557,6 +1655,10 @@ var VMenuPlugin = class _VMenuPlugin {
|
|
|
1557
1655
|
this.disconnected = new Signal(() => this.resource, "disconnected");
|
|
1558
1656
|
this.registerMenu(this.rootMenu);
|
|
1559
1657
|
}
|
|
1658
|
+
/** Menus added with `addStandaloneMenu`. */
|
|
1659
|
+
get standaloneMenus() {
|
|
1660
|
+
return this.standalone;
|
|
1661
|
+
}
|
|
1560
1662
|
/** Creates the plugin. One per resource: a second call returns the first instance. */
|
|
1561
1663
|
static create(displayName) {
|
|
1562
1664
|
if (instance) {
|
|
@@ -1591,6 +1693,38 @@ var VMenuPlugin = class _VMenuPlugin {
|
|
|
1591
1693
|
emitLocal(PluginEvents.Probe);
|
|
1592
1694
|
return this.firstResult;
|
|
1593
1695
|
}
|
|
1696
|
+
/**
|
|
1697
|
+
* Adds a top level menu of your own, outside vMenu's Plugins menu. It has no row anywhere and no toggle key: only your
|
|
1698
|
+
* code opens it, through `menu.requestOpen()`. Needs a vMenu that knows standalone menus, an older one refuses the
|
|
1699
|
+
* registration. Adding one after connecting appears live.
|
|
1700
|
+
*
|
|
1701
|
+
* @param id Letters, digits and underscores, unique within your plugin. "root" and "m" followed by digits are taken
|
|
1702
|
+
* by the menus the API names itself.
|
|
1703
|
+
* @throws When the id is malformed, reserved or already used, when the plugin already has 50 standalone menus, or when
|
|
1704
|
+
* it is connected to a vMenu that predates standalone menus.
|
|
1705
|
+
*/
|
|
1706
|
+
addStandaloneMenu(id, title, subtitle) {
|
|
1707
|
+
if (!/^[A-Za-z0-9_]+$/.test(id)) {
|
|
1708
|
+
throw new Error("A standalone menu id may only contain letters, digits and underscores.");
|
|
1709
|
+
}
|
|
1710
|
+
if (id === "root" || /^m\d+$/.test(id)) {
|
|
1711
|
+
throw new Error(`'${id}' is reserved for the menus the API names itself.`);
|
|
1712
|
+
}
|
|
1713
|
+
if (this.menusById.has(id)) {
|
|
1714
|
+
throw new Error(`A menu with id '${id}' already exists.`);
|
|
1715
|
+
}
|
|
1716
|
+
if (this.standalone.length >= MAX_STANDALONE_MENUS) {
|
|
1717
|
+
throw new Error(`A plugin may have at most ${MAX_STANDALONE_MENUS} standalone menus.`);
|
|
1718
|
+
}
|
|
1719
|
+
if (this.connected && this.vMenuProtocol < PROTOCOL_VERSION) {
|
|
1720
|
+
throw new Error("The vMenu this plugin is connected to predates standalone menus. Update vMenu.");
|
|
1721
|
+
}
|
|
1722
|
+
const menu = new PluginMenu(this, { id, title: Text.toRef(title), subtitle: Text.toRef(subtitle), items: [] }, title, subtitle);
|
|
1723
|
+
this.registerMenu(menu);
|
|
1724
|
+
this.standalone.push(menu);
|
|
1725
|
+
this.emitOp({ op: UpdateOps.AddStandaloneMenu, menu: menu.node });
|
|
1726
|
+
return menu;
|
|
1727
|
+
}
|
|
1594
1728
|
/** Shows a message through vMenu's notification area, credited to your resource. */
|
|
1595
1729
|
notify(style, text, durationMs) {
|
|
1596
1730
|
if (!this.connected) {
|
|
@@ -1687,6 +1821,9 @@ var VMenuPlugin = class _VMenuPlugin {
|
|
|
1687
1821
|
if (this.carriedByPendingAdd(op)) {
|
|
1688
1822
|
return;
|
|
1689
1823
|
}
|
|
1824
|
+
if (op.op === UpdateOps.AddStandaloneMenu && op.menu) {
|
|
1825
|
+
this.pendingMenus.add(op.menu.id);
|
|
1826
|
+
}
|
|
1690
1827
|
if (op.op === UpdateOps.AddItems) {
|
|
1691
1828
|
for (const node of op.items ?? []) {
|
|
1692
1829
|
this.pendingItems.add(node.id);
|
|
@@ -1698,6 +1835,19 @@ var VMenuPlugin = class _VMenuPlugin {
|
|
|
1698
1835
|
batch.push(op);
|
|
1699
1836
|
}
|
|
1700
1837
|
/** @internal */
|
|
1838
|
+
requestOpen(menu, priority, doNotClose) {
|
|
1839
|
+
if (!this.connected) {
|
|
1840
|
+
return Promise.resolve(OpenMenuResult.NotConnected);
|
|
1841
|
+
}
|
|
1842
|
+
if (this.vMenuProtocol < PROTOCOL_VERSION) {
|
|
1843
|
+
return Promise.resolve(OpenMenuResult.NotSupported);
|
|
1844
|
+
}
|
|
1845
|
+
const requestId = ++this.nextOpen;
|
|
1846
|
+
const pending = new Promise((resolve) => this.pendingOpens.set(requestId, resolve));
|
|
1847
|
+
this.emitOp({ op: UpdateOps.OpenMenu, menuId: menu.id, priority, doNotClose: doNotClose || void 0, requestId });
|
|
1848
|
+
return pending;
|
|
1849
|
+
}
|
|
1850
|
+
/** @internal */
|
|
1701
1851
|
filterChanged(menu) {
|
|
1702
1852
|
if (!this.connected) {
|
|
1703
1853
|
return;
|
|
@@ -1767,9 +1917,15 @@ var VMenuPlugin = class _VMenuPlugin {
|
|
|
1767
1917
|
return false;
|
|
1768
1918
|
}
|
|
1769
1919
|
send(ops) {
|
|
1770
|
-
const payload = ops.map(
|
|
1771
|
-
(op
|
|
1772
|
-
|
|
1920
|
+
const payload = ops.map((op) => {
|
|
1921
|
+
if (op.op === UpdateOps.AddItems && op.items) {
|
|
1922
|
+
return { ...op, items: op.items.map(PluginMenu.asAdded) };
|
|
1923
|
+
}
|
|
1924
|
+
if (op.op === UpdateOps.AddStandaloneMenu && op.menu) {
|
|
1925
|
+
return { ...op, menu: PluginMenu.withoutItems(op.menu) };
|
|
1926
|
+
}
|
|
1927
|
+
return op;
|
|
1928
|
+
});
|
|
1773
1929
|
emitLocal(PluginEvents.Update, JSON.stringify({ ops: payload }));
|
|
1774
1930
|
}
|
|
1775
1931
|
ensureHandlers() {
|
|
@@ -1777,23 +1933,30 @@ var VMenuPlugin = class _VMenuPlugin {
|
|
|
1777
1933
|
return;
|
|
1778
1934
|
}
|
|
1779
1935
|
this.handlersRegistered = true;
|
|
1780
|
-
onLocal(PluginEvents.Ready, () => this.
|
|
1781
|
-
onLocal(PluginEvents.readyFor(this.resource), () => this.
|
|
1936
|
+
onLocal(PluginEvents.Ready, (protocolVersion) => this.onReady(protocolVersion));
|
|
1937
|
+
onLocal(PluginEvents.readyFor(this.resource), (protocolVersion) => this.onReady(protocolVersion));
|
|
1782
1938
|
onLocal(PluginEvents.registerResultFor(this.resource), (json) => this.onRegisterResult(json));
|
|
1783
1939
|
onLocal(PluginEvents.eventFor(this.resource), (json) => this.onCallback(json));
|
|
1784
1940
|
onLocal(PluginEvents.promptResultFor(this.resource), (json) => this.onPromptResult(json));
|
|
1941
|
+
onLocal(PluginEvents.openResultFor(this.resource), (json) => this.onOpenResult(json));
|
|
1785
1942
|
onLocal(PluginEvents.themesFor(this.resource), (json) => this.themes.handle(json));
|
|
1786
1943
|
onLocal("onResourceStop", (stopped) => this.onResourceStop(stopped));
|
|
1787
1944
|
}
|
|
1945
|
+
onReady(protocolVersion) {
|
|
1946
|
+
this.vMenuProtocol = typeof protocolVersion === "number" ? protocolVersion : 0;
|
|
1947
|
+
this.sendRegistration();
|
|
1948
|
+
}
|
|
1788
1949
|
sendRegistration() {
|
|
1789
1950
|
emitLocal(PluginEvents.Register, JSON.stringify(this.buildRequest()));
|
|
1790
1951
|
}
|
|
1791
1952
|
buildRequest() {
|
|
1792
1953
|
const request = {
|
|
1793
|
-
|
|
1954
|
+
// Only a plugin that needs a newer vMenu says so, so everything else still registers with an older one.
|
|
1955
|
+
protocolVersion: this.standalone.length > 0 ? PROTOCOL_VERSION : BASE_PROTOCOL_VERSION,
|
|
1794
1956
|
displayName: Text.toRef(this.displayName),
|
|
1795
1957
|
descriptionKey: this.descriptionKey,
|
|
1796
1958
|
menu: this.rootMenu.node,
|
|
1959
|
+
standaloneMenus: this.standalone.length > 0 ? this.standalone.map((menu) => menu.node) : void 0,
|
|
1797
1960
|
playerActions: this.playerActions.nodes.length > 0 ? this.playerActions.nodes : void 0
|
|
1798
1961
|
};
|
|
1799
1962
|
if (Object.keys(this.translations.tables).length > 0) {
|
|
@@ -1819,6 +1982,7 @@ var VMenuPlugin = class _VMenuPlugin {
|
|
|
1819
1982
|
this.connected = result.accepted;
|
|
1820
1983
|
if (!this.connected) {
|
|
1821
1984
|
this.cancelPendingPrompts();
|
|
1985
|
+
this.cancelPendingOpens();
|
|
1822
1986
|
} else {
|
|
1823
1987
|
this.batch(() => {
|
|
1824
1988
|
for (const menu of this.menusById.values()) {
|
|
@@ -1841,7 +2005,9 @@ var VMenuPlugin = class _VMenuPlugin {
|
|
|
1841
2005
|
switch (callback.type) {
|
|
1842
2006
|
case CallbackTypes.MenuOpened:
|
|
1843
2007
|
case CallbackTypes.MenuClosed:
|
|
1844
|
-
case CallbackTypes.MenuIndexChanged:
|
|
2008
|
+
case CallbackTypes.MenuIndexChanged:
|
|
2009
|
+
case CallbackTypes.MenuReplaced:
|
|
2010
|
+
case CallbackTypes.MenuForceClosed: {
|
|
1845
2011
|
const menu = callback.menuId === void 0 ? void 0 : this.menusById.get(callback.menuId);
|
|
1846
2012
|
menu?.handleMenu(callback);
|
|
1847
2013
|
break;
|
|
@@ -1875,6 +2041,25 @@ var VMenuPlugin = class _VMenuPlugin {
|
|
|
1875
2041
|
pending(result);
|
|
1876
2042
|
}
|
|
1877
2043
|
}
|
|
2044
|
+
onOpenResult(json) {
|
|
2045
|
+
const result = parseJson(json);
|
|
2046
|
+
if (!result) {
|
|
2047
|
+
return;
|
|
2048
|
+
}
|
|
2049
|
+
const pending = this.pendingOpens.get(result.requestId);
|
|
2050
|
+
if (!pending) {
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
this.pendingOpens.delete(result.requestId);
|
|
2054
|
+
const known = Object.values(OpenMenuResult);
|
|
2055
|
+
pending(known.includes(result.reason) ? result.reason : OpenMenuResult.BlockedByPriority);
|
|
2056
|
+
}
|
|
2057
|
+
cancelPendingOpens() {
|
|
2058
|
+
for (const pending of this.pendingOpens.values()) {
|
|
2059
|
+
pending(OpenMenuResult.NotConnected);
|
|
2060
|
+
}
|
|
2061
|
+
this.pendingOpens.clear();
|
|
2062
|
+
}
|
|
1878
2063
|
cancelPendingPrompts() {
|
|
1879
2064
|
for (const pending of this.pendingPrompts.values()) {
|
|
1880
2065
|
pending({ requestId: 0, cancelled: true, busy: false });
|
|
@@ -1887,12 +2072,15 @@ var VMenuPlugin = class _VMenuPlugin {
|
|
|
1887
2072
|
}
|
|
1888
2073
|
this.connected = false;
|
|
1889
2074
|
this.cancelPendingPrompts();
|
|
2075
|
+
this.cancelPendingOpens();
|
|
1890
2076
|
this.disconnected.fire();
|
|
1891
2077
|
}
|
|
1892
2078
|
};
|
|
1893
2079
|
export {
|
|
1894
2080
|
Gate,
|
|
2081
|
+
MenuPriority,
|
|
1895
2082
|
NotifyStyle,
|
|
2083
|
+
OpenMenuResult,
|
|
1896
2084
|
PluginBoolSetting,
|
|
1897
2085
|
PluginButton,
|
|
1898
2086
|
PluginCheckbox,
|
package/dist/server.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/version.ts
|
|
2
|
-
var version = true ? "1.0.
|
|
2
|
+
var version = true ? "1.0.15" : "0.0.0-local";
|
|
3
3
|
|
|
4
4
|
// src/format.ts
|
|
5
5
|
function formatFloat(value) {
|
|
@@ -27,6 +27,7 @@ var PluginEvents = {
|
|
|
27
27
|
registerResultFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:RegisterResult`,
|
|
28
28
|
eventFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:Event`,
|
|
29
29
|
promptResultFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:PromptResult`,
|
|
30
|
+
openResultFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:OpenResult`,
|
|
30
31
|
themesFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:Themes`,
|
|
31
32
|
themesRegisteredFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:ThemesRegistered`,
|
|
32
33
|
serverReadyFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:Server:Ready`,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { UpdateOp } from '../protocol.js';
|
|
2
2
|
import type { PluginItem } from './item.js';
|
|
3
3
|
import type { PluginKey } from './key.js';
|
|
4
|
-
import type { PluginMenu } from './menu.js';
|
|
4
|
+
import type { MenuPriority, OpenMenuResult, PluginMenu } from './menu.js';
|
|
5
5
|
export interface PluginHost {
|
|
6
6
|
readonly resource: string;
|
|
7
7
|
readonly isConnected: boolean;
|
|
@@ -12,6 +12,7 @@ export interface PluginHost {
|
|
|
12
12
|
registerKey(key: PluginKey): void;
|
|
13
13
|
unregisterItem(item: PluginItem): void;
|
|
14
14
|
emitOp(op: UpdateOp): void;
|
|
15
|
+
requestOpen(menu: PluginMenu, priority: MenuPriority, doNotClose: boolean): Promise<OpenMenuResult>;
|
|
15
16
|
emitAdd(menu: PluginMenu, item: PluginItem, add: UpdateOp): void;
|
|
16
17
|
filterChanged(menu: PluginMenu): void;
|
|
17
18
|
mergeTranslations(code: string, entries: Record<string, string>): void;
|
|
@@ -7,7 +7,7 @@ export { PluginSetting, PluginBoolSetting, PluginIntSetting, PluginFloatSetting,
|
|
|
7
7
|
export { PluginItem, type ItemOptions } from './item.js';
|
|
8
8
|
export { PluginButton, PluginConfirmButton, PluginCheckbox, PluginList, PluginConfirmList, PluginSlider, PluginDynamicList, PluginSeparator, PluginSubmenu, } from './items.js';
|
|
9
9
|
export { PluginKey, type PluginKeyPress } from './key.js';
|
|
10
|
-
export { PluginMenu, type CheckboxOptions, type ConfirmOptions, type KeyOptions, type ListOptions, type SliderOptions, type SubmenuOptions, } from './menu.js';
|
|
10
|
+
export { MenuPriority, OpenMenuResult, PluginMenu, type CheckboxOptions, type ConfirmOptions, type KeyOptions, type ListOptions, type SliderOptions, type SubmenuOptions, } from './menu.js';
|
|
11
11
|
export { PluginPlayerActions, PluginPlayerButton, PluginPlayerConfirmButton, PluginPlayerList, type PlayerTarget, } from './playerActions.js';
|
|
12
12
|
export { PluginThemes, type PluginTheme } from './themes.js';
|
|
13
13
|
export { PluginTranslations } from './translations.js';
|
|
@@ -45,8 +45,45 @@ export interface KeyOptions {
|
|
|
45
45
|
/** A game control index to suppress while the menu is open, for a default key the game already uses. */
|
|
46
46
|
shadowedControl?: number;
|
|
47
47
|
}
|
|
48
|
+
/** How hard `requestOpenWithPriority` may push. vMenu's own menus always count as `None`. The plain strings work too. */
|
|
49
|
+
export declare const MenuPriority: {
|
|
50
|
+
/** Only opens when no vMenu or plugin menu is open. */
|
|
51
|
+
readonly None: 'none';
|
|
52
|
+
/** Replaces vMenu's menus and other menus at low priority. */
|
|
53
|
+
readonly Low: 'low';
|
|
54
|
+
/** Replaces anything at low or medium priority. */
|
|
55
|
+
readonly Medium: 'medium';
|
|
56
|
+
/**
|
|
57
|
+
* Replaces anything that is not locked. Only use this in extreme situations, prevents vMenu or other plugins with a
|
|
58
|
+
* lower priority from opening any menus while yours is open. Use responsibly!
|
|
59
|
+
*/
|
|
60
|
+
readonly High: 'high';
|
|
61
|
+
};
|
|
62
|
+
/** How hard `requestOpenWithPriority` may push. */
|
|
63
|
+
export type MenuPriority = (typeof MenuPriority)[keyof typeof MenuPriority];
|
|
64
|
+
/** What happened to a request to open a menu. The plain strings work too. */
|
|
65
|
+
export declare const OpenMenuResult: {
|
|
66
|
+
/** The menu is on screen. */
|
|
67
|
+
readonly Opened: 'opened';
|
|
68
|
+
/** A menu with a higher priority is open, or any menu is open for a request at `None`. */
|
|
69
|
+
readonly BlockedByPriority: 'blockedByPriority';
|
|
70
|
+
/** Another menu asked not to be closed. */
|
|
71
|
+
readonly BlockedByLock: 'blockedByLock';
|
|
72
|
+
/** vMenu does not know this menu, for example an empty root menu, which has no row yet. */
|
|
73
|
+
readonly UnknownMenu: 'unknownMenu';
|
|
74
|
+
/** The player is not allowed into this menu, for example because a gate on the row leading to it fails. */
|
|
75
|
+
readonly NotAllowed: 'notAllowed';
|
|
76
|
+
/** A later open or close in the same batch took its place. */
|
|
77
|
+
readonly Superseded: 'superseded';
|
|
78
|
+
/** The plugin is not connected to vMenu, or vMenu stopped before it answered. */
|
|
79
|
+
readonly NotConnected: 'notConnected';
|
|
80
|
+
/** The vMenu this plugin talks to predates open priorities. Nothing was opened, use `open()` there instead. */
|
|
81
|
+
readonly NotSupported: 'notSupported';
|
|
82
|
+
};
|
|
83
|
+
/** What happened to a request to open a menu. */
|
|
84
|
+
export type OpenMenuResult = (typeof OpenMenuResult)[keyof typeof OpenMenuResult];
|
|
48
85
|
/**
|
|
49
|
-
* One of your plugin's menus: the root under your row in vMenu's Plugins menu, or a submenu. Rows
|
|
86
|
+
* One of your plugin's menus: the root under your row in vMenu's Plugins menu, a standalone menu, or a submenu. Rows
|
|
50
87
|
* added before connecting ride along with the registration, rows added later appear live.
|
|
51
88
|
*/
|
|
52
89
|
export declare class PluginMenu {
|
|
@@ -58,6 +95,8 @@ export declare class PluginMenu {
|
|
|
58
95
|
private readonly opened;
|
|
59
96
|
private readonly closed;
|
|
60
97
|
private readonly indexChanged;
|
|
98
|
+
private readonly replaced;
|
|
99
|
+
private readonly forceClosed;
|
|
61
100
|
private insertIndex?;
|
|
62
101
|
private filterFn?;
|
|
63
102
|
/** The menu's id, how vMenu refers to it. */
|
|
@@ -126,8 +165,33 @@ export declare class PluginMenu {
|
|
|
126
165
|
remove(item: PluginItem): void;
|
|
127
166
|
/** Removes every row. */
|
|
128
167
|
clear(): void;
|
|
129
|
-
/** Opens this menu on screen, closing whatever vMenu menu was open. */
|
|
168
|
+
/** Opens this menu on screen at low priority, closing whatever vMenu menu was open. Use `requestOpen` to learn whether it worked. */
|
|
130
169
|
open(): void;
|
|
170
|
+
/**
|
|
171
|
+
* Opens this menu only when no vMenu or plugin menu is open, so it never interrupts the player. Switching between your
|
|
172
|
+
* own menus always works.
|
|
173
|
+
*/
|
|
174
|
+
requestOpen(): Promise<OpenMenuResult>;
|
|
175
|
+
/**
|
|
176
|
+
* Opens this menu, closing an open menu of equal or lower priority. A menu with a higher priority, or one that asked
|
|
177
|
+
* not to be closed, keeps the screen and the request is refused.
|
|
178
|
+
*
|
|
179
|
+
* @param priority `High`: only use this in extreme situations, prevents vMenu or other plugins with a lower priority
|
|
180
|
+
* from opening any menus while yours is open. Use responsibly!
|
|
181
|
+
* @param doNotClose Only with `High`. Only use this in extreme situations. It blocks the player from using anything
|
|
182
|
+
* else, so always close the menu as soon as possible. The lock ends when you close the menu or open its parent, and
|
|
183
|
+
* the player can still force it shut by holding back.
|
|
184
|
+
* @throws When `doNotClose` is set below `High`.
|
|
185
|
+
*/
|
|
186
|
+
requestOpenWithPriority(priority: MenuPriority, doNotClose?: boolean): Promise<OpenMenuResult>;
|
|
187
|
+
/** Called when another plugin's menu took the screen while the player was in this one. `onClosed` fires as well. */
|
|
188
|
+
onReplaced(handler: () => void): Unsubscribe;
|
|
189
|
+
/**
|
|
190
|
+
* Called when this menu was opened with `doNotClose` and closed by anything other than your own code: the player
|
|
191
|
+
* holding back to force it shut, or vMenu having to close it. Whatever the player was in the middle of was left
|
|
192
|
+
* unfinished.
|
|
193
|
+
*/
|
|
194
|
+
onForceClosed(handler: () => void): Unsubscribe;
|
|
131
195
|
/** Closes this plugin's menu if one is open. */
|
|
132
196
|
close(): void;
|
|
133
197
|
/**
|
|
@@ -42,6 +42,8 @@ export declare class VMenuPlugin {
|
|
|
42
42
|
private readonly menusById;
|
|
43
43
|
private readonly keysById;
|
|
44
44
|
private readonly pendingPrompts;
|
|
45
|
+
private readonly pendingOpens;
|
|
46
|
+
private readonly standalone;
|
|
45
47
|
private readonly pendingItems;
|
|
46
48
|
private readonly pendingMenus;
|
|
47
49
|
private readonly dirtyFilters;
|
|
@@ -54,6 +56,8 @@ export declare class VMenuPlugin {
|
|
|
54
56
|
private nextItem;
|
|
55
57
|
private nextMenu;
|
|
56
58
|
private nextPrompt;
|
|
59
|
+
private nextOpen;
|
|
60
|
+
private vMenuProtocol;
|
|
57
61
|
private handlersRegistered;
|
|
58
62
|
private connected;
|
|
59
63
|
/** The resource this plugin runs in, its identity towards vMenu. */
|
|
@@ -70,6 +74,8 @@ export declare class VMenuPlugin {
|
|
|
70
74
|
readonly themes: PluginThemes;
|
|
71
75
|
/** The menu behind your row in vMenu's Plugins menu. */
|
|
72
76
|
readonly rootMenu: PluginMenu;
|
|
77
|
+
/** Menus added with `addStandaloneMenu`. */
|
|
78
|
+
get standaloneMenus(): readonly PluginMenu[];
|
|
73
79
|
/** Actions added to every player's entry of vMenu's Online Players menu. */
|
|
74
80
|
readonly playerActions: PluginPlayerActions;
|
|
75
81
|
private constructor();
|
|
@@ -87,6 +93,17 @@ export declare class VMenuPlugin {
|
|
|
87
93
|
* `accepted` false.
|
|
88
94
|
*/
|
|
89
95
|
connect(): Promise<RegisterResult>;
|
|
96
|
+
/**
|
|
97
|
+
* Adds a top level menu of your own, outside vMenu's Plugins menu. It has no row anywhere and no toggle key: only your
|
|
98
|
+
* code opens it, through `menu.requestOpen()`. Needs a vMenu that knows standalone menus, an older one refuses the
|
|
99
|
+
* registration. Adding one after connecting appears live.
|
|
100
|
+
*
|
|
101
|
+
* @param id Letters, digits and underscores, unique within your plugin. "root" and "m" followed by digits are taken
|
|
102
|
+
* by the menus the API names itself.
|
|
103
|
+
* @throws When the id is malformed, reserved or already used, when the plugin already has 50 standalone menus, or when
|
|
104
|
+
* it is connected to a vMenu that predates standalone menus.
|
|
105
|
+
*/
|
|
106
|
+
addStandaloneMenu(id: string, title: TextLike, subtitle?: TextLike): PluginMenu;
|
|
90
107
|
/** Shows a message through vMenu's notification area, credited to your resource. */
|
|
91
108
|
notify(style: NotifyStyle, text: TextLike, durationMs?: number): void;
|
|
92
109
|
/** Asks the player for text through vMenu's input box. Resolves to null if they cancelled or the box was unavailable. */
|
|
@@ -108,12 +125,15 @@ export declare class VMenuPlugin {
|
|
|
108
125
|
private carriedByPendingAdd;
|
|
109
126
|
private send;
|
|
110
127
|
private ensureHandlers;
|
|
128
|
+
private onReady;
|
|
111
129
|
private sendRegistration;
|
|
112
130
|
private buildRequest;
|
|
113
131
|
private onRegisterResult;
|
|
114
132
|
private onCallback;
|
|
115
133
|
private itemOrUndefined;
|
|
116
134
|
private onPromptResult;
|
|
135
|
+
private onOpenResult;
|
|
136
|
+
private cancelPendingOpens;
|
|
117
137
|
private cancelPendingPrompts;
|
|
118
138
|
private onResourceStop;
|
|
119
139
|
}
|
package/dist/types/protocol.d.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
export declare const PROTOCOL_VERSION =
|
|
1
|
+
export declare const PROTOCOL_VERSION = 3;
|
|
2
|
+
/** What a plugin without standalone menus announces, so it still registers with a vMenu that predates them. */
|
|
3
|
+
export declare const BASE_PROTOCOL_VERSION = 2;
|
|
4
|
+
export declare const MAX_STANDALONE_MENUS = 50;
|
|
2
5
|
export declare const SERVER_PROTOCOL_VERSION = 1;
|
|
3
6
|
export declare const VMENU_RESOURCE = "vMenu.Enhanced";
|
|
4
7
|
export declare const PluginEvents: {
|
|
@@ -19,6 +22,7 @@ export declare const PluginEvents: {
|
|
|
19
22
|
readonly registerResultFor: (resource: string) => string;
|
|
20
23
|
readonly eventFor: (resource: string) => string;
|
|
21
24
|
readonly promptResultFor: (resource: string) => string;
|
|
25
|
+
readonly openResultFor: (resource: string) => string;
|
|
22
26
|
readonly themesFor: (resource: string) => string;
|
|
23
27
|
readonly themesRegisteredFor: (resource: string) => string;
|
|
24
28
|
readonly serverReadyFor: (resource: string) => string;
|
|
@@ -40,6 +44,8 @@ export declare const NodeEvents: {
|
|
|
40
44
|
readonly Opened: 'opened';
|
|
41
45
|
readonly Closed: 'closed';
|
|
42
46
|
readonly IndexChanged: 'indexChanged';
|
|
47
|
+
readonly Replaced: 'replaced';
|
|
48
|
+
readonly ForceClosed: 'forceClosed';
|
|
43
49
|
readonly Highlighted: 'highlighted';
|
|
44
50
|
};
|
|
45
51
|
export declare const SettingTypes: {
|
|
@@ -62,11 +68,27 @@ export declare const CallbackTypes: {
|
|
|
62
68
|
readonly MenuOpened: 'menuOpened';
|
|
63
69
|
readonly MenuClosed: 'menuClosed';
|
|
64
70
|
readonly MenuIndexChanged: 'menuIndexChanged';
|
|
71
|
+
readonly MenuReplaced: 'menuReplaced';
|
|
72
|
+
readonly MenuForceClosed: 'menuForceClosed';
|
|
65
73
|
readonly PlayerActionSelected: 'playerActionSelected';
|
|
66
74
|
readonly PlayerActionConfirmed: 'playerActionConfirmed';
|
|
67
75
|
readonly PlayerActionListSelected: 'playerActionListSelected';
|
|
68
76
|
readonly KeyPressed: 'keyPressed';
|
|
69
77
|
};
|
|
78
|
+
export declare const MenuPriorities: {
|
|
79
|
+
readonly None: 'none';
|
|
80
|
+
readonly Low: 'low';
|
|
81
|
+
readonly Medium: 'medium';
|
|
82
|
+
readonly High: 'high';
|
|
83
|
+
};
|
|
84
|
+
export declare const OpenMenuReasons: {
|
|
85
|
+
readonly Opened: 'opened';
|
|
86
|
+
readonly BlockedByPriority: 'blockedByPriority';
|
|
87
|
+
readonly BlockedByLock: 'blockedByLock';
|
|
88
|
+
readonly UnknownMenu: 'unknownMenu';
|
|
89
|
+
readonly NotAllowed: 'notAllowed';
|
|
90
|
+
readonly Superseded: 'superseded';
|
|
91
|
+
};
|
|
70
92
|
export declare const UpdateOps: {
|
|
71
93
|
readonly SetText: 'setText';
|
|
72
94
|
readonly SetDescription: 'setDescription';
|
|
@@ -98,6 +120,7 @@ export declare const UpdateOps: {
|
|
|
98
120
|
readonly SetMenuEvents: 'setMenuEvents';
|
|
99
121
|
readonly SetFilter: 'setFilter';
|
|
100
122
|
readonly ClearFilter: 'clearFilter';
|
|
123
|
+
readonly AddStandaloneMenu: 'addStandaloneMenu';
|
|
101
124
|
readonly AddKeys: 'addKeys';
|
|
102
125
|
readonly SetKeyText: 'setKeyText';
|
|
103
126
|
readonly SetKeyEnabled: 'setKeyEnabled';
|
|
@@ -180,12 +203,16 @@ export interface UpdateOp {
|
|
|
180
203
|
options?: TextRef[];
|
|
181
204
|
gate?: GateNode;
|
|
182
205
|
items?: ItemNode[];
|
|
206
|
+
menu?: MenuNode;
|
|
183
207
|
itemIds?: string[];
|
|
184
208
|
beforeItemId?: string;
|
|
185
209
|
events?: string[];
|
|
186
210
|
keys?: KeyNode[];
|
|
187
211
|
language?: string;
|
|
188
212
|
entries?: Record<string, string>;
|
|
213
|
+
priority?: string;
|
|
214
|
+
doNotClose?: boolean;
|
|
215
|
+
requestId?: number;
|
|
189
216
|
}
|
|
190
217
|
export interface PluginCallback {
|
|
191
218
|
type: string;
|
|
@@ -213,6 +240,7 @@ export interface RegisterRequest {
|
|
|
213
240
|
translations?: Record<string, Record<string, string>>;
|
|
214
241
|
settings?: SettingNode[];
|
|
215
242
|
menu?: MenuNode;
|
|
243
|
+
standaloneMenus?: MenuNode[];
|
|
216
244
|
playerActions?: ItemNode[];
|
|
217
245
|
}
|
|
218
246
|
/** vMenu's answer to a registration. Errors mean it was refused, warnings mean it was accepted with parts skipped. */
|
|
@@ -238,6 +266,11 @@ export interface PromptRequest {
|
|
|
238
266
|
}[];
|
|
239
267
|
}[];
|
|
240
268
|
}
|
|
269
|
+
export interface OpenMenuResultPayload {
|
|
270
|
+
requestId: number;
|
|
271
|
+
opened: boolean;
|
|
272
|
+
reason: string;
|
|
273
|
+
}
|
|
241
274
|
export interface PromptResult {
|
|
242
275
|
requestId: number;
|
|
243
276
|
cancelled: boolean;
|
package/dist/vmenu.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// vMenu Enhanced plugin API for JavaScript, built for vMenu Enhanced v1.0.
|
|
1
|
+
// vMenu Enhanced plugin API for JavaScript, built for vMenu Enhanced v1.0.15.
|
|
2
2
|
// Copy this file into your resource and load it before your own scripts, see https://docs.vespura.com/vmenu/enhanced/plugins/javascript/
|
|
3
3
|
"use strict";
|
|
4
4
|
var vMenu = (() => {
|
|
@@ -24,7 +24,9 @@ var vMenu = (() => {
|
|
|
24
24
|
var iife_exports = {};
|
|
25
25
|
__export(iife_exports, {
|
|
26
26
|
Gate: () => Gate,
|
|
27
|
+
MenuPriority: () => MenuPriority,
|
|
27
28
|
NotifyStyle: () => NotifyStyle,
|
|
29
|
+
OpenMenuResult: () => OpenMenuResult,
|
|
28
30
|
PluginBoolSetting: () => PluginBoolSetting,
|
|
29
31
|
PluginButton: () => PluginButton,
|
|
30
32
|
PluginCheckbox: () => PluginCheckbox,
|
|
@@ -57,7 +59,7 @@ var vMenu = (() => {
|
|
|
57
59
|
});
|
|
58
60
|
|
|
59
61
|
// src/version.ts
|
|
60
|
-
var version = true ? "1.0.
|
|
62
|
+
var version = true ? "1.0.15" : "0.0.0-local";
|
|
61
63
|
|
|
62
64
|
// src/text.ts
|
|
63
65
|
var Text = class _Text {
|
|
@@ -192,7 +194,9 @@ var vMenu = (() => {
|
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
// src/protocol.ts
|
|
195
|
-
var PROTOCOL_VERSION =
|
|
197
|
+
var PROTOCOL_VERSION = 3;
|
|
198
|
+
var BASE_PROTOCOL_VERSION = 2;
|
|
199
|
+
var MAX_STANDALONE_MENUS = 50;
|
|
196
200
|
var SERVER_PROTOCOL_VERSION = 1;
|
|
197
201
|
var VMENU_RESOURCE = "vMenu.Enhanced";
|
|
198
202
|
var PluginEvents = {
|
|
@@ -213,6 +217,7 @@ var vMenu = (() => {
|
|
|
213
217
|
registerResultFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:RegisterResult`,
|
|
214
218
|
eventFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:Event`,
|
|
215
219
|
promptResultFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:PromptResult`,
|
|
220
|
+
openResultFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:OpenResult`,
|
|
216
221
|
themesFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:Themes`,
|
|
217
222
|
themesRegisteredFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:ThemesRegistered`,
|
|
218
223
|
serverReadyFor: (resource) => `vMenu.Enhanced:Plugins:${resource}:Server:Ready`,
|
|
@@ -234,6 +239,8 @@ var vMenu = (() => {
|
|
|
234
239
|
Opened: "opened",
|
|
235
240
|
Closed: "closed",
|
|
236
241
|
IndexChanged: "indexChanged",
|
|
242
|
+
Replaced: "replaced",
|
|
243
|
+
ForceClosed: "forceClosed",
|
|
237
244
|
Highlighted: "highlighted"
|
|
238
245
|
};
|
|
239
246
|
var SettingTypes = {
|
|
@@ -256,6 +263,8 @@ var vMenu = (() => {
|
|
|
256
263
|
MenuOpened: "menuOpened",
|
|
257
264
|
MenuClosed: "menuClosed",
|
|
258
265
|
MenuIndexChanged: "menuIndexChanged",
|
|
266
|
+
MenuReplaced: "menuReplaced",
|
|
267
|
+
MenuForceClosed: "menuForceClosed",
|
|
259
268
|
PlayerActionSelected: "playerActionSelected",
|
|
260
269
|
PlayerActionConfirmed: "playerActionConfirmed",
|
|
261
270
|
PlayerActionListSelected: "playerActionListSelected",
|
|
@@ -292,6 +301,7 @@ var vMenu = (() => {
|
|
|
292
301
|
SetMenuEvents: "setMenuEvents",
|
|
293
302
|
SetFilter: "setFilter",
|
|
294
303
|
ClearFilter: "clearFilter",
|
|
304
|
+
AddStandaloneMenu: "addStandaloneMenu",
|
|
295
305
|
AddKeys: "addKeys",
|
|
296
306
|
SetKeyText: "setKeyText",
|
|
297
307
|
SetKeyEnabled: "setKeyEnabled",
|
|
@@ -974,6 +984,37 @@ var vMenu = (() => {
|
|
|
974
984
|
};
|
|
975
985
|
|
|
976
986
|
// src/client/menu.ts
|
|
987
|
+
var MenuPriority = {
|
|
988
|
+
/** Only opens when no vMenu or plugin menu is open. */
|
|
989
|
+
None: "none",
|
|
990
|
+
/** Replaces vMenu's menus and other menus at low priority. */
|
|
991
|
+
Low: "low",
|
|
992
|
+
/** Replaces anything at low or medium priority. */
|
|
993
|
+
Medium: "medium",
|
|
994
|
+
/**
|
|
995
|
+
* Replaces anything that is not locked. Only use this in extreme situations, prevents vMenu or other plugins with a
|
|
996
|
+
* lower priority from opening any menus while yours is open. Use responsibly!
|
|
997
|
+
*/
|
|
998
|
+
High: "high"
|
|
999
|
+
};
|
|
1000
|
+
var OpenMenuResult = {
|
|
1001
|
+
/** The menu is on screen. */
|
|
1002
|
+
Opened: "opened",
|
|
1003
|
+
/** A menu with a higher priority is open, or any menu is open for a request at `None`. */
|
|
1004
|
+
BlockedByPriority: "blockedByPriority",
|
|
1005
|
+
/** Another menu asked not to be closed. */
|
|
1006
|
+
BlockedByLock: "blockedByLock",
|
|
1007
|
+
/** vMenu does not know this menu, for example an empty root menu, which has no row yet. */
|
|
1008
|
+
UnknownMenu: "unknownMenu",
|
|
1009
|
+
/** The player is not allowed into this menu, for example because a gate on the row leading to it fails. */
|
|
1010
|
+
NotAllowed: "notAllowed",
|
|
1011
|
+
/** A later open or close in the same batch took its place. */
|
|
1012
|
+
Superseded: "superseded",
|
|
1013
|
+
/** The plugin is not connected to vMenu, or vMenu stopped before it answered. */
|
|
1014
|
+
NotConnected: "notConnected",
|
|
1015
|
+
/** The vMenu this plugin talks to predates open priorities. Nothing was opened, use `open()` there instead. */
|
|
1016
|
+
NotSupported: "notSupported"
|
|
1017
|
+
};
|
|
977
1018
|
var PluginMenu = class _PluginMenu {
|
|
978
1019
|
/** @internal */
|
|
979
1020
|
constructor(plugin, node, title, subtitle) {
|
|
@@ -986,6 +1027,8 @@ var vMenu = (() => {
|
|
|
986
1027
|
this.opened = new Signal(() => plugin.resource, "opened");
|
|
987
1028
|
this.closed = new Signal(() => plugin.resource, "closed");
|
|
988
1029
|
this.indexChanged = new Signal(() => plugin.resource, "indexChanged");
|
|
1030
|
+
this.replaced = new Signal(() => plugin.resource, "replaced");
|
|
1031
|
+
this.forceClosed = new Signal(() => plugin.resource, "forceClosed");
|
|
989
1032
|
}
|
|
990
1033
|
/** The menu's id, how vMenu refers to it. */
|
|
991
1034
|
get id() {
|
|
@@ -1213,10 +1256,53 @@ var vMenu = (() => {
|
|
|
1213
1256
|
this.node.items.length = 0;
|
|
1214
1257
|
this.plugin.emitOp({ op: UpdateOps.ClearMenu, menuId: this.id });
|
|
1215
1258
|
}
|
|
1216
|
-
/** Opens this menu on screen, closing whatever vMenu menu was open. */
|
|
1259
|
+
/** Opens this menu on screen at low priority, closing whatever vMenu menu was open. Use `requestOpen` to learn whether it worked. */
|
|
1217
1260
|
open() {
|
|
1218
1261
|
this.plugin.emitOp({ op: UpdateOps.OpenMenu, menuId: this.id });
|
|
1219
1262
|
}
|
|
1263
|
+
/**
|
|
1264
|
+
* Opens this menu only when no vMenu or plugin menu is open, so it never interrupts the player. Switching between your
|
|
1265
|
+
* own menus always works.
|
|
1266
|
+
*/
|
|
1267
|
+
requestOpen() {
|
|
1268
|
+
return this.requestOpenWithPriority(MenuPriority.None);
|
|
1269
|
+
}
|
|
1270
|
+
/**
|
|
1271
|
+
* Opens this menu, closing an open menu of equal or lower priority. A menu with a higher priority, or one that asked
|
|
1272
|
+
* not to be closed, keeps the screen and the request is refused.
|
|
1273
|
+
*
|
|
1274
|
+
* @param priority `High`: only use this in extreme situations, prevents vMenu or other plugins with a lower priority
|
|
1275
|
+
* from opening any menus while yours is open. Use responsibly!
|
|
1276
|
+
* @param doNotClose Only with `High`. Only use this in extreme situations. It blocks the player from using anything
|
|
1277
|
+
* else, so always close the menu as soon as possible. The lock ends when you close the menu or open its parent, and
|
|
1278
|
+
* the player can still force it shut by holding back.
|
|
1279
|
+
* @throws When `doNotClose` is set below `High`.
|
|
1280
|
+
*/
|
|
1281
|
+
requestOpenWithPriority(priority, doNotClose = false) {
|
|
1282
|
+
if (!Object.values(MenuPriority).includes(priority)) {
|
|
1283
|
+
throw new Error(`'${String(priority)}' is not a menu priority.`);
|
|
1284
|
+
}
|
|
1285
|
+
if (doNotClose && priority !== MenuPriority.High) {
|
|
1286
|
+
throw new Error("doNotClose only works with MenuPriority.High.");
|
|
1287
|
+
}
|
|
1288
|
+
return this.plugin.requestOpen(this, priority, doNotClose);
|
|
1289
|
+
}
|
|
1290
|
+
/** Called when another plugin's menu took the screen while the player was in this one. `onClosed` fires as well. */
|
|
1291
|
+
onReplaced(handler) {
|
|
1292
|
+
const unsubscribe = this.replaced.add(handler);
|
|
1293
|
+
this.subscribeMenuEvent(NodeEvents.Replaced);
|
|
1294
|
+
return unsubscribe;
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* Called when this menu was opened with `doNotClose` and closed by anything other than your own code: the player
|
|
1298
|
+
* holding back to force it shut, or vMenu having to close it. Whatever the player was in the middle of was left
|
|
1299
|
+
* unfinished.
|
|
1300
|
+
*/
|
|
1301
|
+
onForceClosed(handler) {
|
|
1302
|
+
const unsubscribe = this.forceClosed.add(handler);
|
|
1303
|
+
this.subscribeMenuEvent(NodeEvents.ForceClosed);
|
|
1304
|
+
return unsubscribe;
|
|
1305
|
+
}
|
|
1220
1306
|
/** Closes this plugin's menu if one is open. */
|
|
1221
1307
|
close() {
|
|
1222
1308
|
this.plugin.emitOp({ op: UpdateOps.CloseMenu, menuId: this.id });
|
|
@@ -1260,6 +1346,12 @@ var vMenu = (() => {
|
|
|
1260
1346
|
this.indexChanged.fire(callback.oldIndex ?? 0, callback.newIndex);
|
|
1261
1347
|
}
|
|
1262
1348
|
break;
|
|
1349
|
+
case CallbackTypes.MenuReplaced:
|
|
1350
|
+
this.replaced.fire();
|
|
1351
|
+
break;
|
|
1352
|
+
case CallbackTypes.MenuForceClosed:
|
|
1353
|
+
this.forceClosed.fire();
|
|
1354
|
+
break;
|
|
1263
1355
|
}
|
|
1264
1356
|
}
|
|
1265
1357
|
/** @internal */
|
|
@@ -1267,8 +1359,12 @@ var vMenu = (() => {
|
|
|
1267
1359
|
if (!node.menu) {
|
|
1268
1360
|
return node;
|
|
1269
1361
|
}
|
|
1270
|
-
|
|
1271
|
-
|
|
1362
|
+
return { ...node, menu: _PluginMenu.withoutItems(node.menu) };
|
|
1363
|
+
}
|
|
1364
|
+
/** @internal */
|
|
1365
|
+
static withoutItems(node) {
|
|
1366
|
+
const { items: _items, ...menu } = node;
|
|
1367
|
+
return { ...menu, items: [] };
|
|
1272
1368
|
}
|
|
1273
1369
|
newNode(type, text, options) {
|
|
1274
1370
|
return { id: options?.id ?? this.plugin.nextItemId(), type, text: Text.toRef(text) };
|
|
@@ -1598,6 +1694,8 @@ var vMenu = (() => {
|
|
|
1598
1694
|
this.menusById = /* @__PURE__ */ new Map();
|
|
1599
1695
|
this.keysById = /* @__PURE__ */ new Map();
|
|
1600
1696
|
this.pendingPrompts = /* @__PURE__ */ new Map();
|
|
1697
|
+
this.pendingOpens = /* @__PURE__ */ new Map();
|
|
1698
|
+
this.standalone = [];
|
|
1601
1699
|
this.pendingItems = /* @__PURE__ */ new Set();
|
|
1602
1700
|
this.pendingMenus = /* @__PURE__ */ new Set();
|
|
1603
1701
|
this.dirtyFilters = [];
|
|
@@ -1605,6 +1703,8 @@ var vMenu = (() => {
|
|
|
1605
1703
|
this.nextItem = 0;
|
|
1606
1704
|
this.nextMenu = 0;
|
|
1607
1705
|
this.nextPrompt = 0;
|
|
1706
|
+
this.nextOpen = 0;
|
|
1707
|
+
this.vMenuProtocol = 0;
|
|
1608
1708
|
this.handlersRegistered = false;
|
|
1609
1709
|
this.connected = false;
|
|
1610
1710
|
this.resource = currentResource();
|
|
@@ -1618,6 +1718,10 @@ var vMenu = (() => {
|
|
|
1618
1718
|
this.disconnected = new Signal(() => this.resource, "disconnected");
|
|
1619
1719
|
this.registerMenu(this.rootMenu);
|
|
1620
1720
|
}
|
|
1721
|
+
/** Menus added with `addStandaloneMenu`. */
|
|
1722
|
+
get standaloneMenus() {
|
|
1723
|
+
return this.standalone;
|
|
1724
|
+
}
|
|
1621
1725
|
/** Creates the plugin. One per resource: a second call returns the first instance. */
|
|
1622
1726
|
static create(displayName) {
|
|
1623
1727
|
if (instance) {
|
|
@@ -1652,6 +1756,38 @@ var vMenu = (() => {
|
|
|
1652
1756
|
emitLocal(PluginEvents.Probe);
|
|
1653
1757
|
return this.firstResult;
|
|
1654
1758
|
}
|
|
1759
|
+
/**
|
|
1760
|
+
* Adds a top level menu of your own, outside vMenu's Plugins menu. It has no row anywhere and no toggle key: only your
|
|
1761
|
+
* code opens it, through `menu.requestOpen()`. Needs a vMenu that knows standalone menus, an older one refuses the
|
|
1762
|
+
* registration. Adding one after connecting appears live.
|
|
1763
|
+
*
|
|
1764
|
+
* @param id Letters, digits and underscores, unique within your plugin. "root" and "m" followed by digits are taken
|
|
1765
|
+
* by the menus the API names itself.
|
|
1766
|
+
* @throws When the id is malformed, reserved or already used, when the plugin already has 50 standalone menus, or when
|
|
1767
|
+
* it is connected to a vMenu that predates standalone menus.
|
|
1768
|
+
*/
|
|
1769
|
+
addStandaloneMenu(id, title, subtitle) {
|
|
1770
|
+
if (!/^[A-Za-z0-9_]+$/.test(id)) {
|
|
1771
|
+
throw new Error("A standalone menu id may only contain letters, digits and underscores.");
|
|
1772
|
+
}
|
|
1773
|
+
if (id === "root" || /^m\d+$/.test(id)) {
|
|
1774
|
+
throw new Error(`'${id}' is reserved for the menus the API names itself.`);
|
|
1775
|
+
}
|
|
1776
|
+
if (this.menusById.has(id)) {
|
|
1777
|
+
throw new Error(`A menu with id '${id}' already exists.`);
|
|
1778
|
+
}
|
|
1779
|
+
if (this.standalone.length >= MAX_STANDALONE_MENUS) {
|
|
1780
|
+
throw new Error(`A plugin may have at most ${MAX_STANDALONE_MENUS} standalone menus.`);
|
|
1781
|
+
}
|
|
1782
|
+
if (this.connected && this.vMenuProtocol < PROTOCOL_VERSION) {
|
|
1783
|
+
throw new Error("The vMenu this plugin is connected to predates standalone menus. Update vMenu.");
|
|
1784
|
+
}
|
|
1785
|
+
const menu = new PluginMenu(this, { id, title: Text.toRef(title), subtitle: Text.toRef(subtitle), items: [] }, title, subtitle);
|
|
1786
|
+
this.registerMenu(menu);
|
|
1787
|
+
this.standalone.push(menu);
|
|
1788
|
+
this.emitOp({ op: UpdateOps.AddStandaloneMenu, menu: menu.node });
|
|
1789
|
+
return menu;
|
|
1790
|
+
}
|
|
1655
1791
|
/** Shows a message through vMenu's notification area, credited to your resource. */
|
|
1656
1792
|
notify(style, text, durationMs) {
|
|
1657
1793
|
if (!this.connected) {
|
|
@@ -1748,6 +1884,9 @@ var vMenu = (() => {
|
|
|
1748
1884
|
if (this.carriedByPendingAdd(op)) {
|
|
1749
1885
|
return;
|
|
1750
1886
|
}
|
|
1887
|
+
if (op.op === UpdateOps.AddStandaloneMenu && op.menu) {
|
|
1888
|
+
this.pendingMenus.add(op.menu.id);
|
|
1889
|
+
}
|
|
1751
1890
|
if (op.op === UpdateOps.AddItems) {
|
|
1752
1891
|
for (const node of op.items ?? []) {
|
|
1753
1892
|
this.pendingItems.add(node.id);
|
|
@@ -1759,6 +1898,19 @@ var vMenu = (() => {
|
|
|
1759
1898
|
batch.push(op);
|
|
1760
1899
|
}
|
|
1761
1900
|
/** @internal */
|
|
1901
|
+
requestOpen(menu, priority, doNotClose) {
|
|
1902
|
+
if (!this.connected) {
|
|
1903
|
+
return Promise.resolve(OpenMenuResult.NotConnected);
|
|
1904
|
+
}
|
|
1905
|
+
if (this.vMenuProtocol < PROTOCOL_VERSION) {
|
|
1906
|
+
return Promise.resolve(OpenMenuResult.NotSupported);
|
|
1907
|
+
}
|
|
1908
|
+
const requestId = ++this.nextOpen;
|
|
1909
|
+
const pending = new Promise((resolve) => this.pendingOpens.set(requestId, resolve));
|
|
1910
|
+
this.emitOp({ op: UpdateOps.OpenMenu, menuId: menu.id, priority, doNotClose: doNotClose || void 0, requestId });
|
|
1911
|
+
return pending;
|
|
1912
|
+
}
|
|
1913
|
+
/** @internal */
|
|
1762
1914
|
filterChanged(menu) {
|
|
1763
1915
|
if (!this.connected) {
|
|
1764
1916
|
return;
|
|
@@ -1828,9 +1980,15 @@ var vMenu = (() => {
|
|
|
1828
1980
|
return false;
|
|
1829
1981
|
}
|
|
1830
1982
|
send(ops) {
|
|
1831
|
-
const payload = ops.map(
|
|
1832
|
-
(op
|
|
1833
|
-
|
|
1983
|
+
const payload = ops.map((op) => {
|
|
1984
|
+
if (op.op === UpdateOps.AddItems && op.items) {
|
|
1985
|
+
return { ...op, items: op.items.map(PluginMenu.asAdded) };
|
|
1986
|
+
}
|
|
1987
|
+
if (op.op === UpdateOps.AddStandaloneMenu && op.menu) {
|
|
1988
|
+
return { ...op, menu: PluginMenu.withoutItems(op.menu) };
|
|
1989
|
+
}
|
|
1990
|
+
return op;
|
|
1991
|
+
});
|
|
1834
1992
|
emitLocal(PluginEvents.Update, JSON.stringify({ ops: payload }));
|
|
1835
1993
|
}
|
|
1836
1994
|
ensureHandlers() {
|
|
@@ -1838,23 +1996,30 @@ var vMenu = (() => {
|
|
|
1838
1996
|
return;
|
|
1839
1997
|
}
|
|
1840
1998
|
this.handlersRegistered = true;
|
|
1841
|
-
onLocal(PluginEvents.Ready, () => this.
|
|
1842
|
-
onLocal(PluginEvents.readyFor(this.resource), () => this.
|
|
1999
|
+
onLocal(PluginEvents.Ready, (protocolVersion) => this.onReady(protocolVersion));
|
|
2000
|
+
onLocal(PluginEvents.readyFor(this.resource), (protocolVersion) => this.onReady(protocolVersion));
|
|
1843
2001
|
onLocal(PluginEvents.registerResultFor(this.resource), (json) => this.onRegisterResult(json));
|
|
1844
2002
|
onLocal(PluginEvents.eventFor(this.resource), (json) => this.onCallback(json));
|
|
1845
2003
|
onLocal(PluginEvents.promptResultFor(this.resource), (json) => this.onPromptResult(json));
|
|
2004
|
+
onLocal(PluginEvents.openResultFor(this.resource), (json) => this.onOpenResult(json));
|
|
1846
2005
|
onLocal(PluginEvents.themesFor(this.resource), (json) => this.themes.handle(json));
|
|
1847
2006
|
onLocal("onResourceStop", (stopped) => this.onResourceStop(stopped));
|
|
1848
2007
|
}
|
|
2008
|
+
onReady(protocolVersion) {
|
|
2009
|
+
this.vMenuProtocol = typeof protocolVersion === "number" ? protocolVersion : 0;
|
|
2010
|
+
this.sendRegistration();
|
|
2011
|
+
}
|
|
1849
2012
|
sendRegistration() {
|
|
1850
2013
|
emitLocal(PluginEvents.Register, JSON.stringify(this.buildRequest()));
|
|
1851
2014
|
}
|
|
1852
2015
|
buildRequest() {
|
|
1853
2016
|
const request = {
|
|
1854
|
-
|
|
2017
|
+
// Only a plugin that needs a newer vMenu says so, so everything else still registers with an older one.
|
|
2018
|
+
protocolVersion: this.standalone.length > 0 ? PROTOCOL_VERSION : BASE_PROTOCOL_VERSION,
|
|
1855
2019
|
displayName: Text.toRef(this.displayName),
|
|
1856
2020
|
descriptionKey: this.descriptionKey,
|
|
1857
2021
|
menu: this.rootMenu.node,
|
|
2022
|
+
standaloneMenus: this.standalone.length > 0 ? this.standalone.map((menu) => menu.node) : void 0,
|
|
1858
2023
|
playerActions: this.playerActions.nodes.length > 0 ? this.playerActions.nodes : void 0
|
|
1859
2024
|
};
|
|
1860
2025
|
if (Object.keys(this.translations.tables).length > 0) {
|
|
@@ -1880,6 +2045,7 @@ var vMenu = (() => {
|
|
|
1880
2045
|
this.connected = result.accepted;
|
|
1881
2046
|
if (!this.connected) {
|
|
1882
2047
|
this.cancelPendingPrompts();
|
|
2048
|
+
this.cancelPendingOpens();
|
|
1883
2049
|
} else {
|
|
1884
2050
|
this.batch(() => {
|
|
1885
2051
|
for (const menu of this.menusById.values()) {
|
|
@@ -1902,7 +2068,9 @@ var vMenu = (() => {
|
|
|
1902
2068
|
switch (callback.type) {
|
|
1903
2069
|
case CallbackTypes.MenuOpened:
|
|
1904
2070
|
case CallbackTypes.MenuClosed:
|
|
1905
|
-
case CallbackTypes.MenuIndexChanged:
|
|
2071
|
+
case CallbackTypes.MenuIndexChanged:
|
|
2072
|
+
case CallbackTypes.MenuReplaced:
|
|
2073
|
+
case CallbackTypes.MenuForceClosed: {
|
|
1906
2074
|
const menu = callback.menuId === void 0 ? void 0 : this.menusById.get(callback.menuId);
|
|
1907
2075
|
menu?.handleMenu(callback);
|
|
1908
2076
|
break;
|
|
@@ -1936,6 +2104,25 @@ var vMenu = (() => {
|
|
|
1936
2104
|
pending(result);
|
|
1937
2105
|
}
|
|
1938
2106
|
}
|
|
2107
|
+
onOpenResult(json) {
|
|
2108
|
+
const result = parseJson(json);
|
|
2109
|
+
if (!result) {
|
|
2110
|
+
return;
|
|
2111
|
+
}
|
|
2112
|
+
const pending = this.pendingOpens.get(result.requestId);
|
|
2113
|
+
if (!pending) {
|
|
2114
|
+
return;
|
|
2115
|
+
}
|
|
2116
|
+
this.pendingOpens.delete(result.requestId);
|
|
2117
|
+
const known = Object.values(OpenMenuResult);
|
|
2118
|
+
pending(known.includes(result.reason) ? result.reason : OpenMenuResult.BlockedByPriority);
|
|
2119
|
+
}
|
|
2120
|
+
cancelPendingOpens() {
|
|
2121
|
+
for (const pending of this.pendingOpens.values()) {
|
|
2122
|
+
pending(OpenMenuResult.NotConnected);
|
|
2123
|
+
}
|
|
2124
|
+
this.pendingOpens.clear();
|
|
2125
|
+
}
|
|
1939
2126
|
cancelPendingPrompts() {
|
|
1940
2127
|
for (const pending of this.pendingPrompts.values()) {
|
|
1941
2128
|
pending({ requestId: 0, cancelled: true, busy: false });
|
|
@@ -1948,6 +2135,7 @@ var vMenu = (() => {
|
|
|
1948
2135
|
}
|
|
1949
2136
|
this.connected = false;
|
|
1950
2137
|
this.cancelPendingPrompts();
|
|
2138
|
+
this.cancelPendingOpens();
|
|
1951
2139
|
this.disconnected.fire();
|
|
1952
2140
|
}
|
|
1953
2141
|
};
|