@openvcs/sdk 0.2.11 → 0.2.13
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/lib/runtime/index.d.ts +1 -0
- package/lib/runtime/index.js +3 -1
- package/lib/runtime/menu.d.ts +11 -11
- package/lib/runtime/menu.js +61 -97
- package/lib/runtime/modal.d.ts +74 -0
- package/lib/runtime/modal.js +95 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/index.js +1 -0
- package/lib/types/modal.d.ts +129 -0
- package/lib/types/modal.js +4 -0
- package/lib/types/plugin.d.ts +2 -0
- package/package.json +1 -1
- package/src/lib/runtime/index.ts +1 -0
- package/src/lib/runtime/menu.ts +65 -114
- package/src/lib/runtime/modal.ts +172 -0
- package/src/lib/types/index.ts +1 -0
- package/src/lib/types/modal.ts +152 -0
- package/src/lib/types/plugin.ts +2 -0
- package/test/modal.test.ts +24 -0
package/lib/runtime/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { createDefaultPluginDelegates, createRuntimeDispatcher } from './dispatc
|
|
|
5
5
|
export { isPluginFailure, pluginError } from './errors';
|
|
6
6
|
export { createPluginRuntime } from './factory';
|
|
7
7
|
export { createHost } from './host';
|
|
8
|
+
export { ModalBuilder } from './modal';
|
|
8
9
|
export { bootstrapPluginModule, createRegisteredPluginRuntime, } from './registration';
|
|
9
10
|
export { VcsDelegateBase } from './vcs-delegate-base';
|
|
10
11
|
export type { VcsDelegateAssignments } from './vcs-delegate-metadata';
|
package/lib/runtime/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright © 2025-2026 OpenVCS Contributors
|
|
3
3
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
4
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.notify = exports.invoke = exports.registerAction = exports.showMenu = exports.hideMenu = exports.removeMenu = exports.addMenuSeparator = exports.addMenuItem = exports.createMenu = exports.getOrCreateMenu = exports.getMenu = exports.VcsDelegateBase = exports.createRegisteredPluginRuntime = exports.bootstrapPluginModule = exports.createHost = exports.createPluginRuntime = exports.pluginError = exports.isPluginFailure = exports.createRuntimeDispatcher = exports.createDefaultPluginDelegates = void 0;
|
|
5
|
+
exports.notify = exports.invoke = exports.registerAction = exports.showMenu = exports.hideMenu = exports.removeMenu = exports.addMenuSeparator = exports.addMenuItem = exports.createMenu = exports.getOrCreateMenu = exports.getMenu = exports.VcsDelegateBase = exports.createRegisteredPluginRuntime = exports.bootstrapPluginModule = exports.ModalBuilder = exports.createHost = exports.createPluginRuntime = exports.pluginError = exports.isPluginFailure = exports.createRuntimeDispatcher = exports.createDefaultPluginDelegates = void 0;
|
|
6
6
|
exports.startPluginRuntime = startPluginRuntime;
|
|
7
7
|
var dispatcher_1 = require("./dispatcher");
|
|
8
8
|
Object.defineProperty(exports, "createDefaultPluginDelegates", { enumerable: true, get: function () { return dispatcher_1.createDefaultPluginDelegates; } });
|
|
@@ -14,6 +14,8 @@ var factory_1 = require("./factory");
|
|
|
14
14
|
Object.defineProperty(exports, "createPluginRuntime", { enumerable: true, get: function () { return factory_1.createPluginRuntime; } });
|
|
15
15
|
var host_1 = require("./host");
|
|
16
16
|
Object.defineProperty(exports, "createHost", { enumerable: true, get: function () { return host_1.createHost; } });
|
|
17
|
+
var modal_1 = require("./modal");
|
|
18
|
+
Object.defineProperty(exports, "ModalBuilder", { enumerable: true, get: function () { return modal_1.ModalBuilder; } });
|
|
17
19
|
var registration_1 = require("./registration");
|
|
18
20
|
Object.defineProperty(exports, "bootstrapPluginModule", { enumerable: true, get: function () { return registration_1.bootstrapPluginModule; } });
|
|
19
21
|
Object.defineProperty(exports, "createRegisteredPluginRuntime", { enumerable: true, get: function () { return registration_1.createRegisteredPluginRuntime; } });
|
package/lib/runtime/menu.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ type MenubarMenuOptions = {
|
|
|
5
5
|
before?: string;
|
|
6
6
|
after?: string;
|
|
7
7
|
};
|
|
8
|
-
/** Runs
|
|
8
|
+
/** Runs a registered action handler by id. */
|
|
9
9
|
export declare function runRegisteredAction(actionId: string, ...args: unknown[]): Promise<boolean>;
|
|
10
10
|
export interface MenuHandle {
|
|
11
11
|
id: string;
|
|
@@ -17,26 +17,26 @@ export interface MenuHandle {
|
|
|
17
17
|
}
|
|
18
18
|
/** Returns a menu by id, or null when it does not exist. */
|
|
19
19
|
export declare function getMenu(menuId: string): MenuHandle | null;
|
|
20
|
-
/** Returns a menu by id, creating it
|
|
20
|
+
/** Returns a menu by id, creating it if needed. */
|
|
21
21
|
export declare function getOrCreateMenu(menuId: string, label: string): MenuHandle | null;
|
|
22
|
-
/** Creates a menu at a specific
|
|
22
|
+
/** Creates a menu at a specific position. */
|
|
23
23
|
export declare function createMenu(menuId: string, label: string, options?: MenubarMenuOptions): MenuHandle | null;
|
|
24
|
-
/** Adds one
|
|
24
|
+
/** Adds one item to a menu. */
|
|
25
25
|
export declare function addMenuItem(menuId: string, item: MenubarItem): void;
|
|
26
|
-
/** Adds one separator
|
|
26
|
+
/** Adds one separator to a menu. */
|
|
27
27
|
export declare function addMenuSeparator(menuId: string, beforeAction?: string): void;
|
|
28
|
-
/** Removes one menu
|
|
28
|
+
/** Removes one menu from the registry. */
|
|
29
29
|
export declare function removeMenu(menuId: string): void;
|
|
30
|
-
/** Hides one menu from the
|
|
30
|
+
/** Hides one menu from the registry. */
|
|
31
31
|
export declare function hideMenu(menuId: string): void;
|
|
32
|
-
/** Shows one
|
|
32
|
+
/** Shows one menu from the registry. */
|
|
33
33
|
export declare function showMenu(menuId: string): void;
|
|
34
34
|
/** Registers an action handler by id. */
|
|
35
35
|
export declare function registerAction(id: string, handler: (...args: unknown[]) => unknown): void;
|
|
36
|
-
/** Invokes a host command when
|
|
36
|
+
/** Invokes a host command when a host helper is available. */
|
|
37
37
|
export declare function invoke<T = unknown>(cmd: string, args?: unknown): Promise<T>;
|
|
38
|
-
/** Emits a
|
|
38
|
+
/** Emits a notification when the host helper is available. */
|
|
39
39
|
export declare function notify(msg: string): void;
|
|
40
|
-
/** Builds
|
|
40
|
+
/** Builds SDK delegates from the local menu/action registries. */
|
|
41
41
|
export declare function createMenuPluginDelegates(): PluginDelegates<PluginRuntimeContext>;
|
|
42
42
|
export {};
|
package/lib/runtime/menu.js
CHANGED
|
@@ -18,31 +18,35 @@ exports.createMenuPluginDelegates = createMenuPluginDelegates;
|
|
|
18
18
|
const menus = new Map();
|
|
19
19
|
const menuOrder = [];
|
|
20
20
|
const actionHandlers = new Map();
|
|
21
|
-
let
|
|
22
|
-
/** Returns the OpenVCS
|
|
21
|
+
let syntheticId = 0;
|
|
22
|
+
/** Returns the host-side OpenVCS helper, when the environment provides one. */
|
|
23
23
|
function getOpenVCS() {
|
|
24
24
|
return globalThis.OpenVCS;
|
|
25
25
|
}
|
|
26
|
-
/** Normalizes menu
|
|
26
|
+
/** Normalizes a menu id for stable map lookup. */
|
|
27
27
|
function normalizeMenuId(menuId) {
|
|
28
28
|
return String(menuId || '').trim();
|
|
29
29
|
}
|
|
30
|
-
/** Allocates a stable synthetic id for
|
|
30
|
+
/** Allocates a stable synthetic id for generated menu entries. */
|
|
31
31
|
function allocateSyntheticId(prefix) {
|
|
32
|
-
|
|
33
|
-
return `${prefix}-${
|
|
32
|
+
syntheticId += 1;
|
|
33
|
+
return `${prefix}-${syntheticId}`;
|
|
34
34
|
}
|
|
35
|
-
/** Returns
|
|
35
|
+
/** Returns the stored menu state for one id, if present. */
|
|
36
36
|
function getStoredMenu(menuId) {
|
|
37
37
|
return menus.get(normalizeMenuId(menuId)) || null;
|
|
38
38
|
}
|
|
39
|
-
/**
|
|
39
|
+
/** Removes one menu id from the ordering list. */
|
|
40
|
+
function removeMenuId(menuId) {
|
|
41
|
+
const id = normalizeMenuId(menuId);
|
|
42
|
+
const index = menuOrder.indexOf(id);
|
|
43
|
+
if (index >= 0)
|
|
44
|
+
menuOrder.splice(index, 1);
|
|
45
|
+
}
|
|
46
|
+
/** Inserts one menu id into the ordering list. */
|
|
40
47
|
function placeMenuId(menuId, options) {
|
|
41
48
|
const id = normalizeMenuId(menuId);
|
|
42
|
-
|
|
43
|
-
if (existingIndex >= 0) {
|
|
44
|
-
menuOrder.splice(existingIndex, 1);
|
|
45
|
-
}
|
|
49
|
+
removeMenuId(id);
|
|
46
50
|
const beforeId = normalizeMenuId(options?.before || '');
|
|
47
51
|
const afterId = normalizeMenuId(options?.after || '');
|
|
48
52
|
if (afterId) {
|
|
@@ -61,7 +65,7 @@ function placeMenuId(menuId, options) {
|
|
|
61
65
|
}
|
|
62
66
|
menuOrder.push(id);
|
|
63
67
|
}
|
|
64
|
-
/** Ensures a menu record exists for
|
|
68
|
+
/** Ensures a menu record exists for one id. */
|
|
65
69
|
function ensureStoredMenu(menuId, label, options) {
|
|
66
70
|
const id = normalizeMenuId(menuId);
|
|
67
71
|
const safeLabel = String(label || '').trim() || id;
|
|
@@ -69,46 +73,47 @@ function ensureStoredMenu(menuId, label, options) {
|
|
|
69
73
|
if (!menu) {
|
|
70
74
|
menu = { id, label: safeLabel, items: [] };
|
|
71
75
|
menus.set(id, menu);
|
|
72
|
-
placeMenuId(id, options);
|
|
73
|
-
return menu;
|
|
74
76
|
}
|
|
75
|
-
|
|
77
|
+
else {
|
|
78
|
+
menu.label = safeLabel;
|
|
79
|
+
}
|
|
76
80
|
placeMenuId(id, options);
|
|
77
81
|
return menu;
|
|
78
82
|
}
|
|
79
|
-
/** Finds
|
|
80
|
-
function
|
|
83
|
+
/** Finds a stored item by action id. */
|
|
84
|
+
function findStoredItem(menu, actionId) {
|
|
81
85
|
const id = normalizeMenuId(actionId);
|
|
82
86
|
return menu.items.find((item) => item.action === id) || null;
|
|
83
87
|
}
|
|
84
|
-
/** Inserts a menu item
|
|
88
|
+
/** Inserts a menu item at the requested position. */
|
|
85
89
|
function insertMenuItem(menu, item, before, after) {
|
|
86
90
|
const beforeId = normalizeMenuId(before || '');
|
|
87
91
|
const afterId = normalizeMenuId(after || '');
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
if (
|
|
91
|
-
menu.items.splice(
|
|
92
|
-
}
|
|
93
|
-
menu.items.splice(index, 0, item);
|
|
92
|
+
const removeExisting = () => {
|
|
93
|
+
const index = menu.items.findIndex((entry) => entry.id === item.id || entry.action === item.action);
|
|
94
|
+
if (index >= 0)
|
|
95
|
+
menu.items.splice(index, 1);
|
|
94
96
|
};
|
|
95
97
|
if (beforeId) {
|
|
96
98
|
const beforeIndex = menu.items.findIndex((entry) => entry.action === beforeId);
|
|
97
99
|
if (beforeIndex >= 0) {
|
|
98
|
-
|
|
100
|
+
removeExisting();
|
|
101
|
+
menu.items.splice(beforeIndex, 0, item);
|
|
99
102
|
return;
|
|
100
103
|
}
|
|
101
104
|
}
|
|
102
105
|
if (afterId) {
|
|
103
106
|
const afterIndex = menu.items.findIndex((entry) => entry.action === afterId);
|
|
104
107
|
if (afterIndex >= 0) {
|
|
105
|
-
|
|
108
|
+
removeExisting();
|
|
109
|
+
menu.items.splice(afterIndex + 1, 0, item);
|
|
106
110
|
return;
|
|
107
111
|
}
|
|
108
112
|
}
|
|
109
|
-
|
|
113
|
+
removeExisting();
|
|
114
|
+
menu.items.push(item);
|
|
110
115
|
}
|
|
111
|
-
/** Converts one stored
|
|
116
|
+
/** Converts one stored item into a serializable menu payload element. */
|
|
112
117
|
function serializeMenuItem(item) {
|
|
113
118
|
if (item.hidden)
|
|
114
119
|
return null;
|
|
@@ -132,7 +137,7 @@ function serializeMenuItem(item) {
|
|
|
132
137
|
label: item.label,
|
|
133
138
|
};
|
|
134
139
|
}
|
|
135
|
-
/** Serializes the
|
|
140
|
+
/** Serializes the local registry into plugin menu payloads. */
|
|
136
141
|
function serializeMenus() {
|
|
137
142
|
return menuOrder
|
|
138
143
|
.map((menuId, index) => {
|
|
@@ -150,7 +155,7 @@ function serializeMenus() {
|
|
|
150
155
|
})
|
|
151
156
|
.filter((menu) => Boolean(menu));
|
|
152
157
|
}
|
|
153
|
-
/** Runs
|
|
158
|
+
/** Runs a registered action handler by id. */
|
|
154
159
|
async function runRegisteredAction(actionId, ...args) {
|
|
155
160
|
const id = String(actionId || '').trim();
|
|
156
161
|
if (!id)
|
|
@@ -161,45 +166,33 @@ async function runRegisteredAction(actionId, ...args) {
|
|
|
161
166
|
await handler(...args);
|
|
162
167
|
return true;
|
|
163
168
|
}
|
|
164
|
-
/** Creates a stable
|
|
169
|
+
/** Creates a stable handle for one stored menu. */
|
|
165
170
|
function createMenuHandle(menuId) {
|
|
166
171
|
const id = normalizeMenuId(menuId);
|
|
167
172
|
return {
|
|
168
173
|
id,
|
|
169
174
|
addItem(item) {
|
|
170
|
-
const menu = getStoredMenu(this.id) || ensureStoredMenu(this.id, this.id);
|
|
171
175
|
const label = String(item?.label || '').trim();
|
|
172
176
|
const action = String(item?.action || '').trim();
|
|
173
177
|
if (!label || !action)
|
|
174
178
|
return;
|
|
175
|
-
const
|
|
179
|
+
const menu = getStoredMenu(this.id) || ensureStoredMenu(this.id, this.id);
|
|
180
|
+
insertMenuItem(menu, {
|
|
176
181
|
kind: 'button',
|
|
177
182
|
id: action,
|
|
178
183
|
label,
|
|
179
184
|
title: item.title,
|
|
180
185
|
action,
|
|
181
|
-
};
|
|
182
|
-
insertMenuItem(menu, entry, item.before, item.after);
|
|
183
|
-
const openvcs = getOpenVCS();
|
|
184
|
-
if (!openvcs?.menus)
|
|
185
|
-
return;
|
|
186
|
-
openvcs.menus.getOrCreate(menu.id, menu.label);
|
|
187
|
-
openvcs.menus.addMenuItem(menu.id, item);
|
|
186
|
+
}, item.before, item.after);
|
|
188
187
|
},
|
|
189
188
|
addSeparator(beforeAction) {
|
|
190
189
|
const menu = getStoredMenu(this.id) || ensureStoredMenu(this.id, this.id);
|
|
191
|
-
|
|
190
|
+
insertMenuItem(menu, {
|
|
192
191
|
kind: 'separator',
|
|
193
192
|
id: allocateSyntheticId(`${menu.id}-separator`),
|
|
194
193
|
label: 'Separator',
|
|
195
194
|
content: '—',
|
|
196
|
-
};
|
|
197
|
-
insertMenuItem(menu, entry, beforeAction);
|
|
198
|
-
const openvcs = getOpenVCS();
|
|
199
|
-
if (!openvcs?.menus)
|
|
200
|
-
return;
|
|
201
|
-
openvcs.menus.getOrCreate(menu.id, menu.label);
|
|
202
|
-
openvcs.menus.addMenuSeparator(menu.id, beforeAction);
|
|
195
|
+
}, beforeAction);
|
|
203
196
|
},
|
|
204
197
|
removeItem(actionId) {
|
|
205
198
|
const menu = getStoredMenu(this.id);
|
|
@@ -207,37 +200,22 @@ function createMenuHandle(menuId) {
|
|
|
207
200
|
return;
|
|
208
201
|
const idToRemove = normalizeMenuId(actionId);
|
|
209
202
|
menu.items = menu.items.filter((item) => item.action !== idToRemove);
|
|
210
|
-
const openvcs = getOpenVCS();
|
|
211
|
-
if (openvcs?.menus) {
|
|
212
|
-
const handle = openvcs.menus.get(this.id);
|
|
213
|
-
handle?.removeItem(idToRemove);
|
|
214
|
-
}
|
|
215
203
|
},
|
|
216
204
|
hideItem(actionId) {
|
|
217
205
|
const menu = getStoredMenu(this.id);
|
|
218
206
|
if (!menu)
|
|
219
207
|
return;
|
|
220
|
-
const item =
|
|
208
|
+
const item = findStoredItem(menu, actionId);
|
|
221
209
|
if (item)
|
|
222
210
|
item.hidden = true;
|
|
223
|
-
const openvcs = getOpenVCS();
|
|
224
|
-
if (!openvcs?.menus)
|
|
225
|
-
return;
|
|
226
|
-
const handle = openvcs.menus.get(this.id);
|
|
227
|
-
handle?.hideItem(actionId);
|
|
228
211
|
},
|
|
229
212
|
showItem(actionId) {
|
|
230
213
|
const menu = getStoredMenu(this.id);
|
|
231
214
|
if (!menu)
|
|
232
215
|
return;
|
|
233
|
-
const item =
|
|
216
|
+
const item = findStoredItem(menu, actionId);
|
|
234
217
|
if (item)
|
|
235
218
|
item.hidden = false;
|
|
236
|
-
const openvcs = getOpenVCS();
|
|
237
|
-
if (!openvcs?.menus)
|
|
238
|
-
return;
|
|
239
|
-
const handle = openvcs.menus.get(this.id);
|
|
240
|
-
handle?.showItem(actionId);
|
|
241
219
|
},
|
|
242
220
|
};
|
|
243
221
|
}
|
|
@@ -248,53 +226,41 @@ function getMenu(menuId) {
|
|
|
248
226
|
return null;
|
|
249
227
|
return createMenuHandle(stored.id);
|
|
250
228
|
}
|
|
251
|
-
/** Returns a menu by id, creating it
|
|
229
|
+
/** Returns a menu by id, creating it if needed. */
|
|
252
230
|
function getOrCreateMenu(menuId, label) {
|
|
253
231
|
const stored = ensureStoredMenu(menuId, label);
|
|
254
232
|
return createMenuHandle(stored.id);
|
|
255
233
|
}
|
|
256
|
-
/** Creates a menu at a specific
|
|
234
|
+
/** Creates a menu at a specific position. */
|
|
257
235
|
function createMenu(menuId, label, options) {
|
|
258
236
|
const stored = ensureStoredMenu(menuId, label, options);
|
|
259
237
|
return createMenuHandle(stored.id);
|
|
260
238
|
}
|
|
261
|
-
/** Adds one
|
|
239
|
+
/** Adds one item to a menu. */
|
|
262
240
|
function addMenuItem(menuId, item) {
|
|
263
|
-
|
|
264
|
-
const handle = createMenuHandle(menu.id);
|
|
265
|
-
handle.addItem(item);
|
|
241
|
+
createMenuHandle(menuId).addItem(item);
|
|
266
242
|
}
|
|
267
|
-
/** Adds one separator
|
|
243
|
+
/** Adds one separator to a menu. */
|
|
268
244
|
function addMenuSeparator(menuId, beforeAction) {
|
|
269
|
-
|
|
270
|
-
const handle = createMenuHandle(menu.id);
|
|
271
|
-
handle.addSeparator(beforeAction);
|
|
245
|
+
createMenuHandle(menuId).addSeparator(beforeAction);
|
|
272
246
|
}
|
|
273
|
-
/** Removes one menu
|
|
247
|
+
/** Removes one menu from the registry. */
|
|
274
248
|
function removeMenu(menuId) {
|
|
275
249
|
const id = normalizeMenuId(menuId);
|
|
276
250
|
menus.delete(id);
|
|
277
|
-
|
|
278
|
-
if (index >= 0)
|
|
279
|
-
menuOrder.splice(index, 1);
|
|
280
|
-
const openvcs = getOpenVCS();
|
|
281
|
-
openvcs?.menus?.remove(id);
|
|
251
|
+
removeMenuId(id);
|
|
282
252
|
}
|
|
283
|
-
/** Hides one menu from the
|
|
253
|
+
/** Hides one menu from the registry. */
|
|
284
254
|
function hideMenu(menuId) {
|
|
285
255
|
const menu = getStoredMenu(menuId);
|
|
286
256
|
if (menu)
|
|
287
257
|
menu.hidden = true;
|
|
288
|
-
const openvcs = getOpenVCS();
|
|
289
|
-
openvcs?.menus?.hide(normalizeMenuId(menuId));
|
|
290
258
|
}
|
|
291
|
-
/** Shows one
|
|
259
|
+
/** Shows one menu from the registry. */
|
|
292
260
|
function showMenu(menuId) {
|
|
293
261
|
const menu = getStoredMenu(menuId);
|
|
294
262
|
if (menu)
|
|
295
263
|
menu.hidden = false;
|
|
296
|
-
const openvcs = getOpenVCS();
|
|
297
|
-
openvcs?.menus?.show(normalizeMenuId(menuId));
|
|
298
264
|
}
|
|
299
265
|
/** Registers an action handler by id. */
|
|
300
266
|
function registerAction(id, handler) {
|
|
@@ -302,23 +268,21 @@ function registerAction(id, handler) {
|
|
|
302
268
|
if (!key)
|
|
303
269
|
return;
|
|
304
270
|
actionHandlers.set(key, handler);
|
|
305
|
-
const openvcs = getOpenVCS();
|
|
306
|
-
openvcs?.registerAction(key, handler);
|
|
307
271
|
}
|
|
308
|
-
/** Invokes a host command when
|
|
272
|
+
/** Invokes a host command when a host helper is available. */
|
|
309
273
|
function invoke(cmd, args) {
|
|
310
274
|
const openvcs = getOpenVCS();
|
|
311
275
|
if (!openvcs) {
|
|
312
|
-
return Promise.reject(new Error('OpenVCS not available'));
|
|
276
|
+
return Promise.reject(new Error('OpenVCS host is not available in this runtime'));
|
|
313
277
|
}
|
|
314
278
|
return openvcs.invoke(cmd, args);
|
|
315
279
|
}
|
|
316
|
-
/** Emits a
|
|
280
|
+
/** Emits a notification when the host helper is available. */
|
|
317
281
|
function notify(msg) {
|
|
318
282
|
const openvcs = getOpenVCS();
|
|
319
283
|
openvcs?.notify(msg);
|
|
320
284
|
}
|
|
321
|
-
/** Builds
|
|
285
|
+
/** Builds SDK delegates from the local menu/action registries. */
|
|
322
286
|
function createMenuPluginDelegates() {
|
|
323
287
|
return {
|
|
324
288
|
async 'plugin.get_menus'() {
|
|
@@ -326,9 +290,9 @@ function createMenuPluginDelegates() {
|
|
|
326
290
|
},
|
|
327
291
|
async 'plugin.handle_action'(params) {
|
|
328
292
|
const actionId = String(params?.action_id || '').trim();
|
|
329
|
-
if (
|
|
330
|
-
|
|
331
|
-
|
|
293
|
+
if (actionId) {
|
|
294
|
+
await runRegisteredAction(actionId, params?.payload);
|
|
295
|
+
}
|
|
332
296
|
return null;
|
|
333
297
|
},
|
|
334
298
|
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ModalButtonVariant, ModalContentAlign, ModalInputKind, ModalListRowDefinition, ModalSelectOptionDefinition, PluginModalDefinition } from '../types/modal.js';
|
|
2
|
+
/** Describes the options accepted by `ModalBuilder.button()`. */
|
|
3
|
+
export interface ModalBuilderButtonOptions {
|
|
4
|
+
/** Stores the optional tooltip text. */
|
|
5
|
+
title?: string;
|
|
6
|
+
/** Stores the visual button variant. */
|
|
7
|
+
variant?: ModalButtonVariant;
|
|
8
|
+
/** Stores the alignment hint. */
|
|
9
|
+
align?: ModalContentAlign;
|
|
10
|
+
/** Stores a static payload merged into the action payload. */
|
|
11
|
+
payload?: Record<string, unknown>;
|
|
12
|
+
}
|
|
13
|
+
/** Describes the options accepted by `ModalBuilder.text()`. */
|
|
14
|
+
export interface ModalBuilderTextOptions {
|
|
15
|
+
/** Stores the optional tooltip text. */
|
|
16
|
+
title?: string;
|
|
17
|
+
/** Stores the alignment hint. */
|
|
18
|
+
align?: ModalContentAlign;
|
|
19
|
+
}
|
|
20
|
+
/** Describes the options accepted by `ModalBuilder.input()`. */
|
|
21
|
+
export interface ModalBuilderInputOptions {
|
|
22
|
+
/** Stores the input kind. */
|
|
23
|
+
kind?: ModalInputKind;
|
|
24
|
+
/** Stores the default value. */
|
|
25
|
+
value?: string;
|
|
26
|
+
/** Stores the placeholder text. */
|
|
27
|
+
placeholder?: string;
|
|
28
|
+
/** Stores whether the field is required. */
|
|
29
|
+
required?: boolean;
|
|
30
|
+
/** Stores the alignment hint. */
|
|
31
|
+
align?: ModalContentAlign;
|
|
32
|
+
}
|
|
33
|
+
/** Describes the options accepted by `ModalBuilder.select()`. */
|
|
34
|
+
export interface ModalBuilderSelectOptions {
|
|
35
|
+
/** Stores the available options. */
|
|
36
|
+
options: ModalSelectOptionDefinition[];
|
|
37
|
+
/** Stores the default value. */
|
|
38
|
+
value?: string;
|
|
39
|
+
/** Stores the alignment hint. */
|
|
40
|
+
align?: ModalContentAlign;
|
|
41
|
+
}
|
|
42
|
+
/** Describes the options accepted by `ModalBuilder.list()`. */
|
|
43
|
+
export interface ModalBuilderListOptions {
|
|
44
|
+
/** Stores the list label. */
|
|
45
|
+
label?: string;
|
|
46
|
+
/** Stores the empty-state text. */
|
|
47
|
+
emptyText?: string;
|
|
48
|
+
/** Stores the alignment hint. */
|
|
49
|
+
align?: ModalContentAlign;
|
|
50
|
+
/** Stores the list rows. */
|
|
51
|
+
items: ModalListRowDefinition[];
|
|
52
|
+
}
|
|
53
|
+
/** Builds a structured modal definition with a fluent class API. */
|
|
54
|
+
export declare class ModalBuilder {
|
|
55
|
+
private readonly definition;
|
|
56
|
+
/** Creates a new modal builder with the provided title. */
|
|
57
|
+
constructor(title: string);
|
|
58
|
+
/** Adds a text block to the modal body. */
|
|
59
|
+
text(content: string, options?: ModalBuilderTextOptions): this;
|
|
60
|
+
/** Adds a separator to the modal body. */
|
|
61
|
+
separator(): this;
|
|
62
|
+
/** Adds a button to the modal body. */
|
|
63
|
+
button(id: string, content: string, options?: ModalBuilderButtonOptions): this;
|
|
64
|
+
/** Adds a text input to the modal body. */
|
|
65
|
+
input(id: string, label: string, options?: ModalBuilderInputOptions): this;
|
|
66
|
+
/** Adds a select field to the modal body. */
|
|
67
|
+
select(id: string, label: string, options: ModalBuilderSelectOptions): this;
|
|
68
|
+
/** Adds a list block to the modal body. */
|
|
69
|
+
list(id: string, options: ModalBuilderListOptions): this;
|
|
70
|
+
/** Returns the serialized modal payload. */
|
|
71
|
+
build(): PluginModalDefinition;
|
|
72
|
+
/** Requests the host to open the modal with the current definition. */
|
|
73
|
+
open(): Promise<void>;
|
|
74
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright © 2025-2026 OpenVCS Contributors
|
|
3
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.ModalBuilder = void 0;
|
|
6
|
+
const menu_js_1 = require("./menu.js");
|
|
7
|
+
/** Builds a structured modal definition with a fluent class API. */
|
|
8
|
+
class ModalBuilder {
|
|
9
|
+
definition;
|
|
10
|
+
/** Creates a new modal builder with the provided title. */
|
|
11
|
+
constructor(title) {
|
|
12
|
+
this.definition = {
|
|
13
|
+
title: String(title || '').trim(),
|
|
14
|
+
content: [],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** Adds a text block to the modal body. */
|
|
18
|
+
text(content, options = {}) {
|
|
19
|
+
this.definition.content.push({
|
|
20
|
+
type: 'text',
|
|
21
|
+
content: String(content || ''),
|
|
22
|
+
...(options.title ? { title: options.title } : {}),
|
|
23
|
+
...(options.align ? { align: options.align } : {}),
|
|
24
|
+
});
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
/** Adds a separator to the modal body. */
|
|
28
|
+
separator() {
|
|
29
|
+
this.definition.content.push({ type: 'separator' });
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
/** Adds a button to the modal body. */
|
|
33
|
+
button(id, content, options = {}) {
|
|
34
|
+
this.definition.content.push({
|
|
35
|
+
type: 'button',
|
|
36
|
+
id: String(id || '').trim(),
|
|
37
|
+
content: String(content || ''),
|
|
38
|
+
...(options.title ? { title: options.title } : {}),
|
|
39
|
+
...(options.variant && options.variant !== 'default' ? { variant: options.variant } : {}),
|
|
40
|
+
...(options.align ? { align: options.align } : {}),
|
|
41
|
+
...(options.payload ? { payload: options.payload } : {}),
|
|
42
|
+
});
|
|
43
|
+
return this;
|
|
44
|
+
}
|
|
45
|
+
/** Adds a text input to the modal body. */
|
|
46
|
+
input(id, label, options = {}) {
|
|
47
|
+
this.definition.content.push({
|
|
48
|
+
type: 'input',
|
|
49
|
+
id: String(id || '').trim(),
|
|
50
|
+
label: String(label || '').trim(),
|
|
51
|
+
...(options.kind ? { kind: options.kind } : {}),
|
|
52
|
+
...(options.value !== undefined ? { value: options.value } : {}),
|
|
53
|
+
...(options.placeholder ? { placeholder: options.placeholder } : {}),
|
|
54
|
+
...(options.required ? { required: true } : {}),
|
|
55
|
+
...(options.align ? { align: options.align } : {}),
|
|
56
|
+
});
|
|
57
|
+
return this;
|
|
58
|
+
}
|
|
59
|
+
/** Adds a select field to the modal body. */
|
|
60
|
+
select(id, label, options) {
|
|
61
|
+
this.definition.content.push({
|
|
62
|
+
type: 'select',
|
|
63
|
+
id: String(id || '').trim(),
|
|
64
|
+
label: String(label || '').trim(),
|
|
65
|
+
options: Array.isArray(options.options) ? options.options : [],
|
|
66
|
+
...(options.value !== undefined ? { value: options.value } : {}),
|
|
67
|
+
...(options.align ? { align: options.align } : {}),
|
|
68
|
+
});
|
|
69
|
+
return this;
|
|
70
|
+
}
|
|
71
|
+
/** Adds a list block to the modal body. */
|
|
72
|
+
list(id, options) {
|
|
73
|
+
this.definition.content.push({
|
|
74
|
+
type: 'list',
|
|
75
|
+
id: String(id || '').trim(),
|
|
76
|
+
...(options.label ? { label: options.label } : {}),
|
|
77
|
+
...(options.emptyText ? { emptyText: options.emptyText } : {}),
|
|
78
|
+
...(options.align ? { align: options.align } : {}),
|
|
79
|
+
items: Array.isArray(options.items) ? options.items : [],
|
|
80
|
+
});
|
|
81
|
+
return this;
|
|
82
|
+
}
|
|
83
|
+
/** Returns the serialized modal payload. */
|
|
84
|
+
build() {
|
|
85
|
+
return {
|
|
86
|
+
title: this.definition.title,
|
|
87
|
+
content: this.definition.content.map((item) => ({ ...item })),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/** Requests the host to open the modal with the current definition. */
|
|
91
|
+
async open() {
|
|
92
|
+
await (0, menu_js_1.invoke)('open_plugin_modal', { modal: this.build() });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.ModalBuilder = ModalBuilder;
|
package/lib/types/index.d.ts
CHANGED
package/lib/types/index.js
CHANGED
|
@@ -17,6 +17,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
17
17
|
};
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
19
|
__exportStar(require("./host"), exports);
|
|
20
|
+
__exportStar(require("./modal"), exports);
|
|
20
21
|
__exportStar(require("./menubar"), exports);
|
|
21
22
|
__exportStar(require("./plugin"), exports);
|
|
22
23
|
__exportStar(require("./protocol"), exports);
|