@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.
- package/dist/extension-api/index.js +189 -3
- package/dist/extension-api/parts/ExtensionApiCommandMap/ExtensionApiCommandMap.js +2 -0
- package/dist/extension-api/parts/ViewRegistry/ViewRegistry.js +189 -3
- package/extension-api/dist/index.d.ts +1 -1
- package/extension-api/dist/parts/CommandMap/CommandMap.d.ts +1 -0
- package/extension-api/dist/parts/ExtensionApiCommandMap/ExtensionApiCommandMap.d.ts +1 -0
- package/extension-api/dist/parts/ExtensionApiCommandMap/ExtensionApiCommandMap.js +2 -1
- package/extension-api/dist/parts/ExtensionApiWorkerCommandMap/ExtensionApiWorkerCommandMap.d.ts +1 -0
- package/extension-api/dist/parts/View/View.d.ts +25 -0
- package/extension-api/dist/parts/ViewRegistry/ViewRegistry.d.ts +2 -1
- package/extension-api/dist/parts/ViewRegistry/ViewRegistry.js +189 -3
- package/extension-api/package.json +1 -1
- package/extension-api/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
package/extension-api/dist/parts/ExtensionApiWorkerCommandMap/ExtensionApiWorkerCommandMap.d.ts
CHANGED
|
@@ -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>;
|