@lvce-editor/extension-host-worker 8.34.0 → 8.36.0

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.
@@ -2393,6 +2393,50 @@ var diffTree = (oldNodes, newNodes) => {
2393
2393
  var views = /* @__PURE__ */ Object.create(null);
2394
2394
  var instances = /* @__PURE__ */ Object.create(null);
2395
2395
  var renderedDoms = /* @__PURE__ */ Object.create(null);
2396
+ var contexts = /* @__PURE__ */ Object.create(null);
2397
+ var contextViewIds = /* @__PURE__ */ Object.create(null);
2398
+ var assertBoolean = (value, message) => {
2399
+ if (value !== void 0 && typeof value !== "boolean") {
2400
+ throw new ExtensionApiError(message);
2401
+ }
2402
+ };
2403
+ var assertNumber = (value, message) => {
2404
+ if (typeof value !== "number") {
2405
+ throw new ExtensionApiError(message);
2406
+ }
2407
+ };
2408
+ var assertString = (value, message) => {
2409
+ if (typeof value !== "string" || value.length === 0) {
2410
+ throw new ExtensionApiError(message);
2411
+ }
2412
+ };
2413
+ var assertEventListener = (viewId, listener, index) => {
2414
+ if (!listener || typeof listener !== "object") {
2415
+ throw new ExtensionApiError(`view ${viewId} event listener ${index} must be an object`);
2416
+ }
2417
+ const eventListener = listener;
2418
+ if (typeof eventListener.name !== "string" && typeof eventListener.name !== "number") {
2419
+ throw new ExtensionApiError(`view ${viewId} event listener ${index} is missing name`);
2420
+ }
2421
+ if (!Array.isArray(eventListener.params) || eventListener.params.some((param) => typeof param !== "string")) {
2422
+ throw new ExtensionApiError(`view ${viewId} event listener ${index} is missing params`);
2423
+ }
2424
+ assertBoolean(eventListener.capture, `view ${viewId} event listener ${index} has invalid capture`);
2425
+ assertBoolean(eventListener.passive, `view ${viewId} event listener ${index} has invalid passive`);
2426
+ assertBoolean(eventListener.preventDefault, `view ${viewId} event listener ${index} has invalid preventDefault`);
2427
+ assertBoolean(eventListener.stopPropagation, `view ${viewId} event listener ${index} has invalid stopPropagation`);
2428
+ };
2429
+ var assertEventListeners = (view) => {
2430
+ if (view.eventListeners === void 0) {
2431
+ return;
2432
+ }
2433
+ if (!Array.isArray(view.eventListeners)) {
2434
+ throw new ExtensionApiError(`view ${view.id} eventListeners must be an array`);
2435
+ }
2436
+ for (const [index, listener] of view.eventListeners.entries()) {
2437
+ assertEventListener(view.id, listener, index);
2438
+ }
2439
+ };
2396
2440
  var assertView = (view) => {
2397
2441
  if (!view) {
2398
2442
  throw new ExtensionApiError("view is not defined");
@@ -2406,11 +2450,13 @@ var assertView = (view) => {
2406
2450
  if (view.id in views) {
2407
2451
  throw new ExtensionApiError(`view ${view.id} is already registered`);
2408
2452
  }
2453
+ assertEventListeners(view);
2409
2454
  };
2410
2455
  var toRegisteredView = (view) => {
2411
2456
  const displayName = view.displayName || view.name || view.title;
2412
2457
  const registeredView = {
2413
2458
  displayName,
2459
+ ...view.eventListeners && { eventListeners: view.eventListeners },
2414
2460
  icon: view.icon,
2415
2461
  id: view.id,
2416
2462
  name: view.name,
@@ -2462,6 +2508,34 @@ var renderDom = async (instance) => {
2462
2508
  }
2463
2509
  return dom;
2464
2510
  };
2511
+ var normalizeMenuEntry = (entry, index) => {
2512
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
2513
+ throw new ExtensionApiError(`menu entry ${index} must be an object`);
2514
+ }
2515
+ const menuEntry = entry;
2516
+ assertString(menuEntry.id, `menu entry ${index} is missing id`);
2517
+ assertString(menuEntry.label, `menu entry ${index} is missing label`);
2518
+ assertString(menuEntry.command, `menu entry ${index} is missing command`);
2519
+ if (menuEntry.flags !== void 0 && typeof menuEntry.flags !== "number") {
2520
+ throw new ExtensionApiError(`menu entry ${index} has invalid flags`);
2521
+ }
2522
+ if (menuEntry.args !== void 0 && !Array.isArray(menuEntry.args)) {
2523
+ throw new ExtensionApiError(`menu entry ${index} has invalid args`);
2524
+ }
2525
+ return {
2526
+ command: menuEntry.command,
2527
+ flags: menuEntry.flags ?? 0,
2528
+ id: menuEntry.id,
2529
+ label: menuEntry.label,
2530
+ ...menuEntry.args && { args: menuEntry.args }
2531
+ };
2532
+ };
2533
+ var normalizeMenuEntries = (entries) => {
2534
+ if (!Array.isArray(entries)) {
2535
+ throw new ExtensionApiError("view menu entries must be an array");
2536
+ }
2537
+ return entries.map(normalizeMenuEntry);
2538
+ };
2465
2539
  var renderPatches = async (uid, instance) => {
2466
2540
  const oldDom = renderedDoms[uid] || [];
2467
2541
  const newDom = await renderDom(instance);
@@ -2472,6 +2546,88 @@ var renderPatches = async (uid, instance) => {
2472
2546
  type: "setPatches"
2473
2547
  };
2474
2548
  };
2549
+ var normalizeContext = (context) => {
2550
+ if (!context || typeof context !== "object" || Array.isArray(context)) {
2551
+ return {};
2552
+ }
2553
+ const normalized = {};
2554
+ for (const [key, value] of Object.entries(context)) {
2555
+ if (typeof value === "boolean") {
2556
+ normalized[key] = value;
2557
+ }
2558
+ }
2559
+ return normalized;
2560
+ };
2561
+ var isSameContext = (oldContext, newContext) => {
2562
+ const oldKeys = Object.keys(oldContext);
2563
+ const newKeys = Object.keys(newContext);
2564
+ if (oldKeys.length !== newKeys.length) {
2565
+ return false;
2566
+ }
2567
+ for (const key of oldKeys) {
2568
+ if (oldContext[key] !== newContext[key]) {
2569
+ return false;
2570
+ }
2571
+ }
2572
+ return true;
2573
+ };
2574
+ var unchangedContext = {
2575
+ changed: false,
2576
+ newContext: {},
2577
+ oldContext: {}
2578
+ };
2579
+ var maybeNotifyContextChanged = async (uid, viewId, instance) => {
2580
+ if (typeof instance.getContext !== "function") {
2581
+ return unchangedContext;
2582
+ }
2583
+ const oldContext = contexts[uid] || {};
2584
+ const newContext = normalizeContext(instance.getContext());
2585
+ if (isSameContext(oldContext, newContext)) {
2586
+ return {
2587
+ changed: false,
2588
+ newContext,
2589
+ oldContext
2590
+ };
2591
+ }
2592
+ if (Object.keys(newContext).length === 0) {
2593
+ delete contexts[uid];
2594
+ } else {
2595
+ contexts[uid] = newContext;
2596
+ }
2597
+ await ExtensionManagementWorker_exports.invoke("Extensions.handleViewContextChange", uid, viewId, newContext);
2598
+ return {
2599
+ changed: true,
2600
+ newContext,
2601
+ oldContext
2602
+ };
2603
+ };
2604
+ var renderFocus = async (instance, contextChange) => {
2605
+ if (!contextChange.changed || typeof instance.renderFocus !== "function") {
2606
+ return "";
2607
+ }
2608
+ const focusSelector = await instance.renderFocus(contextChange.oldContext, contextChange.newContext);
2609
+ if (typeof focusSelector !== "string") {
2610
+ throw new ExtensionApiError("view renderFocus result must be a string");
2611
+ }
2612
+ return focusSelector;
2613
+ };
2614
+ var withFocusSelector = async (result, instance, contextChange) => {
2615
+ const focusSelector = await renderFocus(instance, contextChange);
2616
+ if (!focusSelector) {
2617
+ return result;
2618
+ }
2619
+ return {
2620
+ ...result,
2621
+ focusSelector
2622
+ };
2623
+ };
2624
+ var maybeClearContext = async (uid, viewId) => {
2625
+ if (!contexts[uid]) {
2626
+ return;
2627
+ }
2628
+ delete contexts[uid];
2629
+ await ExtensionManagementWorker_exports.invoke("Extensions.handleViewContextChange", uid, viewId, {});
2630
+ };
2475
2631
  var createViewInstance = async (viewId, uid, context) => {
2476
2632
  const view = views[viewId];
2477
2633
  if (!view) {
@@ -2485,36 +2641,51 @@ var createViewInstance = async (viewId, uid, context) => {
2485
2641
  requestRerender() {
2486
2642
  return ExtensionManagementWorker_exports.invoke("Extensions.requestViewRerender", uid);
2487
2643
  },
2644
+ showContextMenu(menuId, x, y) {
2645
+ assertString(menuId, "menuId must be a string");
2646
+ assertNumber(x, "x must be a number");
2647
+ assertNumber(y, "y must be a number");
2648
+ return ExtensionManagementWorker_exports.invoke("Extensions.showViewContextMenu", uid, viewId, menuId, x, y);
2649
+ },
2488
2650
  uid,
2489
2651
  viewId
2490
2652
  });
2491
2653
  assertVirtualDomViewInstance(viewId, instance);
2492
2654
  instances[uid] = instance;
2655
+ contextViewIds[uid] = viewId;
2493
2656
  const dom = await renderDom(instance);
2494
2657
  renderedDoms[uid] = dom;
2495
- return {
2658
+ const result = {
2496
2659
  dom,
2497
2660
  type: "setDom"
2498
2661
  };
2662
+ const contextChange = await maybeNotifyContextChanged(uid, viewId, instance);
2663
+ return withFocusSelector(result, instance, contextChange);
2499
2664
  };
2500
2665
  var dispatchViewEvent = async (uid, event) => {
2501
2666
  const instance = getVirtualDomInstance(uid);
2502
2667
  if (typeof instance.handleEvent === "function") {
2503
2668
  await instance.handleEvent(event);
2504
2669
  }
2505
- return renderPatches(uid, instance);
2670
+ const result = await renderPatches(uid, instance);
2671
+ const contextChange = await maybeNotifyContextChanged(uid, contextViewIds[uid], instance);
2672
+ return withFocusSelector(result, instance, contextChange);
2506
2673
  };
2507
2674
  var renderViewInstance = async (uid) => {
2508
2675
  const instance = getVirtualDomInstance(uid);
2509
- return renderPatches(uid, instance);
2676
+ const result = await renderPatches(uid, instance);
2677
+ const contextChange = await maybeNotifyContextChanged(uid, contextViewIds[uid], instance);
2678
+ return withFocusSelector(result, instance, contextChange);
2510
2679
  };
2511
2680
  var disposeViewInstance = async (uid) => {
2512
2681
  const instance = instances[uid];
2513
2682
  if (instance && typeof instance.dispose === "function") {
2514
2683
  await instance.dispose();
2515
2684
  }
2685
+ await maybeClearContext(uid, contextViewIds[uid]);
2516
2686
  delete instances[uid];
2517
2687
  delete renderedDoms[uid];
2688
+ delete contextViewIds[uid];
2518
2689
  };
2519
2690
  var saveViewInstanceState = async (uid) => {
2520
2691
  const instance = getVirtualDomInstance(uid);
@@ -2523,6 +2694,14 @@ var saveViewInstanceState = async (uid) => {
2523
2694
  }
2524
2695
  return instance.saveState();
2525
2696
  };
2697
+ var getViewMenuEntries = async (uid, menuId) => {
2698
+ assertString(menuId, "menuId must be a string");
2699
+ const instance = getVirtualDomInstance(uid);
2700
+ if (typeof instance.getMenuEntries !== "function") {
2701
+ return [];
2702
+ }
2703
+ return normalizeMenuEntries(await instance.getMenuEntries(menuId));
2704
+ };
2526
2705
  var getViewRegistrySnapshot = () => {
2527
2706
  return {
2528
2707
  views: Object.values(views).map(toRegisteredView)
@@ -2538,6 +2717,12 @@ var resetViewRegistry = () => {
2538
2717
  for (const uid of Object.keys(renderedDoms)) {
2539
2718
  delete renderedDoms[Number(uid)];
2540
2719
  }
2720
+ for (const uid of Object.keys(contexts)) {
2721
+ delete contexts[Number(uid)];
2722
+ }
2723
+ for (const uid of Object.keys(contextViewIds)) {
2724
+ delete contextViewIds[Number(uid)];
2725
+ }
2541
2726
  };
2542
2727
 
2543
2728
  // packages/extension-api/src/parts/ExtensionApiCommandMap/ExtensionApiCommandMap.ts
@@ -2570,6 +2755,7 @@ var commandMap = {
2570
2755
  "ExtensionApi.getOutputChannelRegistrySnapshot": getOutputChannelRegistrySnapshot,
2571
2756
  "ExtensionApi.getSourceControlProviderRegistrySnapshot": getSourceControlProviderRegistrySnapshot,
2572
2757
  "ExtensionApi.getStatusBarItems": getStatusBarItems,
2758
+ "ExtensionApi.getViewMenuEntries": getViewMenuEntries,
2573
2759
  "ExtensionApi.getViewRegistrySnapshot": getViewRegistrySnapshot,
2574
2760
  "ExtensionApi.renderViewInstance": renderViewInstance,
2575
2761
  "ExtensionApi.saveViewInstanceState": saveViewInstanceState
@@ -23,6 +23,7 @@ import {
23
23
  dispatchViewEvent,
24
24
  disposeViewInstance,
25
25
  executeViewProvider,
26
+ getViewMenuEntries,
26
27
  getViewRegistrySnapshot,
27
28
  renderViewInstance,
28
29
  saveViewInstanceState
@@ -56,6 +57,7 @@ const commandMap = {
56
57
  "ExtensionApi.getOutputChannelRegistrySnapshot": getOutputChannelRegistrySnapshot,
57
58
  "ExtensionApi.getSourceControlProviderRegistrySnapshot": getSourceControlProviderRegistrySnapshot,
58
59
  "ExtensionApi.getStatusBarItems": getStatusBarItems,
60
+ "ExtensionApi.getViewMenuEntries": getViewMenuEntries,
59
61
  "ExtensionApi.getViewRegistrySnapshot": getViewRegistrySnapshot,
60
62
  "ExtensionApi.renderViewInstance": renderViewInstance,
61
63
  "ExtensionApi.saveViewInstanceState": saveViewInstanceState
@@ -4,6 +4,50 @@ import { ExtensionApiError } from "../ExtensionApiError/ExtensionApiError.js";
4
4
  const views = /* @__PURE__ */ Object.create(null);
5
5
  const instances = /* @__PURE__ */ Object.create(null);
6
6
  const renderedDoms = /* @__PURE__ */ Object.create(null);
7
+ const contexts = /* @__PURE__ */ Object.create(null);
8
+ const contextViewIds = /* @__PURE__ */ Object.create(null);
9
+ const assertBoolean = (value, message) => {
10
+ if (value !== void 0 && typeof value !== "boolean") {
11
+ throw new ExtensionApiError(message);
12
+ }
13
+ };
14
+ const assertNumber = (value, message) => {
15
+ if (typeof value !== "number") {
16
+ throw new ExtensionApiError(message);
17
+ }
18
+ };
19
+ const assertString = (value, message) => {
20
+ if (typeof value !== "string" || value.length === 0) {
21
+ throw new ExtensionApiError(message);
22
+ }
23
+ };
24
+ const assertEventListener = (viewId, listener, index) => {
25
+ if (!listener || typeof listener !== "object") {
26
+ throw new ExtensionApiError(`view ${viewId} event listener ${index} must be an object`);
27
+ }
28
+ const eventListener = listener;
29
+ if (typeof eventListener.name !== "string" && typeof eventListener.name !== "number") {
30
+ throw new ExtensionApiError(`view ${viewId} event listener ${index} is missing name`);
31
+ }
32
+ if (!Array.isArray(eventListener.params) || eventListener.params.some((param) => typeof param !== "string")) {
33
+ throw new ExtensionApiError(`view ${viewId} event listener ${index} is missing params`);
34
+ }
35
+ assertBoolean(eventListener.capture, `view ${viewId} event listener ${index} has invalid capture`);
36
+ assertBoolean(eventListener.passive, `view ${viewId} event listener ${index} has invalid passive`);
37
+ assertBoolean(eventListener.preventDefault, `view ${viewId} event listener ${index} has invalid preventDefault`);
38
+ assertBoolean(eventListener.stopPropagation, `view ${viewId} event listener ${index} has invalid stopPropagation`);
39
+ };
40
+ const assertEventListeners = (view) => {
41
+ if (view.eventListeners === void 0) {
42
+ return;
43
+ }
44
+ if (!Array.isArray(view.eventListeners)) {
45
+ throw new ExtensionApiError(`view ${view.id} eventListeners must be an array`);
46
+ }
47
+ for (const [index, listener] of view.eventListeners.entries()) {
48
+ assertEventListener(view.id, listener, index);
49
+ }
50
+ };
7
51
  const assertView = (view) => {
8
52
  if (!view) {
9
53
  throw new ExtensionApiError("view is not defined");
@@ -17,11 +61,13 @@ const assertView = (view) => {
17
61
  if (view.id in views) {
18
62
  throw new ExtensionApiError(`view ${view.id} is already registered`);
19
63
  }
64
+ assertEventListeners(view);
20
65
  };
21
66
  const toRegisteredView = (view) => {
22
67
  const displayName = view.displayName || view.name || view.title;
23
68
  const registeredView = {
24
69
  displayName,
70
+ ...view.eventListeners && { eventListeners: view.eventListeners },
25
71
  icon: view.icon,
26
72
  id: view.id,
27
73
  name: view.name,
@@ -73,6 +119,34 @@ const renderDom = async (instance) => {
73
119
  }
74
120
  return dom;
75
121
  };
122
+ const normalizeMenuEntry = (entry, index) => {
123
+ if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
124
+ throw new ExtensionApiError(`menu entry ${index} must be an object`);
125
+ }
126
+ const menuEntry = entry;
127
+ assertString(menuEntry.id, `menu entry ${index} is missing id`);
128
+ assertString(menuEntry.label, `menu entry ${index} is missing label`);
129
+ assertString(menuEntry.command, `menu entry ${index} is missing command`);
130
+ if (menuEntry.flags !== void 0 && typeof menuEntry.flags !== "number") {
131
+ throw new ExtensionApiError(`menu entry ${index} has invalid flags`);
132
+ }
133
+ if (menuEntry.args !== void 0 && !Array.isArray(menuEntry.args)) {
134
+ throw new ExtensionApiError(`menu entry ${index} has invalid args`);
135
+ }
136
+ return {
137
+ command: menuEntry.command,
138
+ flags: menuEntry.flags ?? 0,
139
+ id: menuEntry.id,
140
+ label: menuEntry.label,
141
+ ...menuEntry.args && { args: menuEntry.args }
142
+ };
143
+ };
144
+ const normalizeMenuEntries = (entries) => {
145
+ if (!Array.isArray(entries)) {
146
+ throw new ExtensionApiError("view menu entries must be an array");
147
+ }
148
+ return entries.map(normalizeMenuEntry);
149
+ };
76
150
  const renderPatches = async (uid, instance) => {
77
151
  const oldDom = renderedDoms[uid] || [];
78
152
  const newDom = await renderDom(instance);
@@ -83,6 +157,88 @@ const renderPatches = async (uid, instance) => {
83
157
  type: "setPatches"
84
158
  };
85
159
  };
160
+ const normalizeContext = (context) => {
161
+ if (!context || typeof context !== "object" || Array.isArray(context)) {
162
+ return {};
163
+ }
164
+ const normalized = {};
165
+ for (const [key, value] of Object.entries(context)) {
166
+ if (typeof value === "boolean") {
167
+ normalized[key] = value;
168
+ }
169
+ }
170
+ return normalized;
171
+ };
172
+ const isSameContext = (oldContext, newContext) => {
173
+ const oldKeys = Object.keys(oldContext);
174
+ const newKeys = Object.keys(newContext);
175
+ if (oldKeys.length !== newKeys.length) {
176
+ return false;
177
+ }
178
+ for (const key of oldKeys) {
179
+ if (oldContext[key] !== newContext[key]) {
180
+ return false;
181
+ }
182
+ }
183
+ return true;
184
+ };
185
+ const unchangedContext = {
186
+ changed: false,
187
+ newContext: {},
188
+ oldContext: {}
189
+ };
190
+ const maybeNotifyContextChanged = async (uid, viewId, instance) => {
191
+ if (typeof instance.getContext !== "function") {
192
+ return unchangedContext;
193
+ }
194
+ const oldContext = contexts[uid] || {};
195
+ const newContext = normalizeContext(instance.getContext());
196
+ if (isSameContext(oldContext, newContext)) {
197
+ return {
198
+ changed: false,
199
+ newContext,
200
+ oldContext
201
+ };
202
+ }
203
+ if (Object.keys(newContext).length === 0) {
204
+ delete contexts[uid];
205
+ } else {
206
+ contexts[uid] = newContext;
207
+ }
208
+ await ExtensionManagementWorker.invoke("Extensions.handleViewContextChange", uid, viewId, newContext);
209
+ return {
210
+ changed: true,
211
+ newContext,
212
+ oldContext
213
+ };
214
+ };
215
+ const renderFocus = async (instance, contextChange) => {
216
+ if (!contextChange.changed || typeof instance.renderFocus !== "function") {
217
+ return "";
218
+ }
219
+ const focusSelector = await instance.renderFocus(contextChange.oldContext, contextChange.newContext);
220
+ if (typeof focusSelector !== "string") {
221
+ throw new ExtensionApiError("view renderFocus result must be a string");
222
+ }
223
+ return focusSelector;
224
+ };
225
+ const withFocusSelector = async (result, instance, contextChange) => {
226
+ const focusSelector = await renderFocus(instance, contextChange);
227
+ if (!focusSelector) {
228
+ return result;
229
+ }
230
+ return {
231
+ ...result,
232
+ focusSelector
233
+ };
234
+ };
235
+ const maybeClearContext = async (uid, viewId) => {
236
+ if (!contexts[uid]) {
237
+ return;
238
+ }
239
+ delete contexts[uid];
240
+ await ExtensionManagementWorker.invoke("Extensions.handleViewContextChange", uid, viewId, {});
241
+ };
86
242
  const createViewInstance = async (viewId, uid, context) => {
87
243
  const view = views[viewId];
88
244
  if (!view) {
@@ -96,36 +252,51 @@ const createViewInstance = async (viewId, uid, context) => {
96
252
  requestRerender() {
97
253
  return ExtensionManagementWorker.invoke("Extensions.requestViewRerender", uid);
98
254
  },
255
+ showContextMenu(menuId, x, y) {
256
+ assertString(menuId, "menuId must be a string");
257
+ assertNumber(x, "x must be a number");
258
+ assertNumber(y, "y must be a number");
259
+ return ExtensionManagementWorker.invoke("Extensions.showViewContextMenu", uid, viewId, menuId, x, y);
260
+ },
99
261
  uid,
100
262
  viewId
101
263
  });
102
264
  assertVirtualDomViewInstance(viewId, instance);
103
265
  instances[uid] = instance;
266
+ contextViewIds[uid] = viewId;
104
267
  const dom = await renderDom(instance);
105
268
  renderedDoms[uid] = dom;
106
- return {
269
+ const result = {
107
270
  dom,
108
271
  type: "setDom"
109
272
  };
273
+ const contextChange = await maybeNotifyContextChanged(uid, viewId, instance);
274
+ return withFocusSelector(result, instance, contextChange);
110
275
  };
111
276
  const dispatchViewEvent = async (uid, event) => {
112
277
  const instance = getVirtualDomInstance(uid);
113
278
  if (typeof instance.handleEvent === "function") {
114
279
  await instance.handleEvent(event);
115
280
  }
116
- return renderPatches(uid, instance);
281
+ const result = await renderPatches(uid, instance);
282
+ const contextChange = await maybeNotifyContextChanged(uid, contextViewIds[uid], instance);
283
+ return withFocusSelector(result, instance, contextChange);
117
284
  };
118
285
  const renderViewInstance = async (uid) => {
119
286
  const instance = getVirtualDomInstance(uid);
120
- return renderPatches(uid, instance);
287
+ const result = await renderPatches(uid, instance);
288
+ const contextChange = await maybeNotifyContextChanged(uid, contextViewIds[uid], instance);
289
+ return withFocusSelector(result, instance, contextChange);
121
290
  };
122
291
  const disposeViewInstance = async (uid) => {
123
292
  const instance = instances[uid];
124
293
  if (instance && typeof instance.dispose === "function") {
125
294
  await instance.dispose();
126
295
  }
296
+ await maybeClearContext(uid, contextViewIds[uid]);
127
297
  delete instances[uid];
128
298
  delete renderedDoms[uid];
299
+ delete contextViewIds[uid];
129
300
  };
130
301
  const saveViewInstanceState = async (uid) => {
131
302
  const instance = getVirtualDomInstance(uid);
@@ -134,6 +305,14 @@ const saveViewInstanceState = async (uid) => {
134
305
  }
135
306
  return instance.saveState();
136
307
  };
308
+ const getViewMenuEntries = async (uid, menuId) => {
309
+ assertString(menuId, "menuId must be a string");
310
+ const instance = getVirtualDomInstance(uid);
311
+ if (typeof instance.getMenuEntries !== "function") {
312
+ return [];
313
+ }
314
+ return normalizeMenuEntries(await instance.getMenuEntries(menuId));
315
+ };
137
316
  const getViewRegistrySnapshot = () => {
138
317
  return {
139
318
  views: Object.values(views).map(toRegisteredView)
@@ -149,12 +328,19 @@ const resetViewRegistry = () => {
149
328
  for (const uid of Object.keys(renderedDoms)) {
150
329
  delete renderedDoms[Number(uid)];
151
330
  }
331
+ for (const uid of Object.keys(contexts)) {
332
+ delete contexts[Number(uid)];
333
+ }
334
+ for (const uid of Object.keys(contextViewIds)) {
335
+ delete contextViewIds[Number(uid)];
336
+ }
152
337
  };
153
338
  export {
154
339
  createViewInstance,
155
340
  dispatchViewEvent,
156
341
  disposeViewInstance,
157
342
  executeViewProvider,
343
+ getViewMenuEntries,
158
344
  getViewRegistrySnapshot,
159
345
  registerView,
160
346
  renderViewInstance,
@@ -12,7 +12,7 @@ export { executeSourceControlAcceptInput, executeSourceControlAdd, executeSource
12
12
  export { createViewInstance, dispatchViewEvent, disposeViewInstance, executeViewProvider, getViewRegistrySnapshot, renderViewInstance, registerView, resetViewRegistry, saveViewInstanceState, } from './parts/ViewRegistry/ViewRegistry.ts';
13
13
  export { createOutputChannel, getOutputChannelRegistrySnapshot, resetOutputChannelRegistry } from './parts/OutputChannel/OutputChannel.ts';
14
14
  export { getStatusBarItemProviderRegistrySnapshot, registerStatusBarItemProvider, resetStatusBarItemProviderRegistry, } from './parts/StatusBar/StatusBar.ts';
15
- export type { RegisteredView, View, ViewContext, ViewEvent, ViewKind, ViewRegistrySnapshot, ViewRenderResult, VirtualDomViewInstance, } from './parts/View/View.ts';
15
+ export type { RegisteredView, View, ViewContext, ViewEvent, ViewKind, MenuEntry, ViewRegistrySnapshot, ViewRenderResult, VirtualDomViewInstance, } from './parts/View/View.ts';
16
16
  export { handleExtensionManagementMessagePort } from './parts/HandleExtensionManagementMessagePort/HandleExtensionManagementMessagePort.ts';
17
17
  export type { Command } from './parts/Command/Command.ts';
18
18
  export type { CommandCallback } from './parts/CommandCallback/CommandCallback.ts';
@@ -28,6 +28,7 @@ export declare const commandMap: {
28
28
  'ExtensionApi.getOutputChannelRegistrySnapshot': () => import("../OutputChannelRegistrySnapshot/OutputChannelRegistrySnapshot.ts").OutputChannelRegistrySnapshot;
29
29
  'ExtensionApi.getSourceControlProviderRegistrySnapshot': () => import("../SourceControlProviderRegistrySnapshot/SourceControlProviderRegistrySnapshot.ts").SourceControlProviderRegistrySnapshot;
30
30
  'ExtensionApi.getStatusBarItems': () => readonly import("../StatusBarItem/StatusBarItem.ts").StatusBarItem[];
31
+ 'ExtensionApi.getViewMenuEntries': (uid: number, menuId: string) => Promise<readonly import("../View/View.ts").MenuEntry[]>;
31
32
  'ExtensionApi.getViewRegistrySnapshot': () => import("../View/View.ts").ViewRegistrySnapshot;
32
33
  'ExtensionApi.renderViewInstance': (uid: number) => Promise<import("../View/View.ts").ViewRenderResult>;
33
34
  'ExtensionApi.saveViewInstanceState': (uid: number) => Promise<unknown>;
@@ -27,6 +27,7 @@ export declare const commandMap: {
27
27
  'ExtensionApi.getOutputChannelRegistrySnapshot': () => import("../OutputChannelRegistrySnapshot/OutputChannelRegistrySnapshot.ts").OutputChannelRegistrySnapshot;
28
28
  'ExtensionApi.getSourceControlProviderRegistrySnapshot': () => import("../SourceControlProviderRegistrySnapshot/SourceControlProviderRegistrySnapshot.ts").SourceControlProviderRegistrySnapshot;
29
29
  'ExtensionApi.getStatusBarItems': () => readonly import("../StatusBarItem/StatusBarItem.ts").StatusBarItem[];
30
+ 'ExtensionApi.getViewMenuEntries': (uid: number, menuId: string) => Promise<readonly import("../View/View.ts").MenuEntry[]>;
30
31
  'ExtensionApi.getViewRegistrySnapshot': () => import("../View/View.ts").ViewRegistrySnapshot;
31
32
  'ExtensionApi.renderViewInstance': (uid: number) => Promise<import("../View/View.ts").ViewRenderResult>;
32
33
  'ExtensionApi.saveViewInstanceState': (uid: number) => Promise<unknown>;
@@ -6,7 +6,7 @@ import { getStatusBarItems } from "../GetStatusBarItems/GetStatusBarItems.js";
6
6
  import { executeHoverProvider, getHoverProviderRegistrySnapshot } from "../Hover/Hover.js";
7
7
  import { getOutputChannelRegistrySnapshot } from "../OutputChannel/OutputChannel.js";
8
8
  import { executeSourceControlAcceptInput, executeSourceControlAdd, executeSourceControlDiscard, executeSourceControlGenerateCommitMessage, executeSourceControlGetChangedFiles, executeSourceControlGetFeatures, executeSourceControlGetFileBefore, executeSourceControlGetFileDecorations, executeSourceControlGetGroups, executeSourceControlIsActive, getSourceControlProviderRegistrySnapshot, } from "../SourceControl/SourceControl.js";
9
- import { createViewInstance, dispatchViewEvent, disposeViewInstance, executeViewProvider, getViewRegistrySnapshot, renderViewInstance, saveViewInstanceState, } from "../ViewRegistry/ViewRegistry.js";
9
+ import { createViewInstance, dispatchViewEvent, disposeViewInstance, executeViewProvider, getViewMenuEntries, getViewRegistrySnapshot, renderViewInstance, saveViewInstanceState, } from "../ViewRegistry/ViewRegistry.js";
10
10
  export const commandMap = {
11
11
  'ExtensionApi.createViewInstance': createViewInstance,
12
12
  'ExtensionApi.dispatchViewEvent': dispatchViewEvent,
@@ -36,6 +36,7 @@ export const commandMap = {
36
36
  'ExtensionApi.getOutputChannelRegistrySnapshot': getOutputChannelRegistrySnapshot,
37
37
  'ExtensionApi.getSourceControlProviderRegistrySnapshot': getSourceControlProviderRegistrySnapshot,
38
38
  'ExtensionApi.getStatusBarItems': getStatusBarItems,
39
+ 'ExtensionApi.getViewMenuEntries': getViewMenuEntries,
39
40
  'ExtensionApi.getViewRegistrySnapshot': getViewRegistrySnapshot,
40
41
  'ExtensionApi.renderViewInstance': renderViewInstance,
41
42
  'ExtensionApi.saveViewInstanceState': saveViewInstanceState,
@@ -29,6 +29,7 @@ export declare const commandMap: {
29
29
  'ExtensionApi.getOutputChannelRegistrySnapshot': () => import("../OutputChannelRegistrySnapshot/OutputChannelRegistrySnapshot.ts").OutputChannelRegistrySnapshot;
30
30
  'ExtensionApi.getSourceControlProviderRegistrySnapshot': () => import("../SourceControlProviderRegistrySnapshot/SourceControlProviderRegistrySnapshot.ts").SourceControlProviderRegistrySnapshot;
31
31
  'ExtensionApi.getStatusBarItems': () => readonly import("../StatusBarItem/StatusBarItem.ts").StatusBarItem[];
32
+ 'ExtensionApi.getViewMenuEntries': (uid: number, menuId: string) => Promise<readonly import("../View/View.ts").MenuEntry[]>;
32
33
  'ExtensionApi.getViewRegistrySnapshot': () => import("../View/View.ts").ViewRegistrySnapshot;
33
34
  'ExtensionApi.renderViewInstance': (uid: number) => Promise<import("../View/View.ts").ViewRenderResult>;
34
35
  'ExtensionApi.saveViewInstanceState': (uid: number) => Promise<unknown>;