@lvce-editor/api 8.35.0 → 8.37.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/index.d.ts CHANGED
@@ -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, MenuEntry, ViewRegistrySnapshot, ViewRenderResult, VirtualDomViewInstance, } from './parts/View/View.ts';
15
+ export type { RegisteredView, View, ViewAction, 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.getViewActions': (uid: number) => Promise<readonly import("../View/View.ts").ViewAction[]>;
31
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>;
@@ -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.getViewActions': (uid: number) => Promise<readonly import("../View/View.ts").ViewAction[]>;
30
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>;
@@ -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, getViewMenuEntries, getViewRegistrySnapshot, renderViewInstance, saveViewInstanceState, } from "../ViewRegistry/ViewRegistry.js";
9
+ import { createViewInstance, dispatchViewEvent, disposeViewInstance, executeViewProvider, getViewActions, 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.getViewActions': getViewActions,
39
40
  'ExtensionApi.getViewMenuEntries': getViewMenuEntries,
40
41
  'ExtensionApi.getViewRegistrySnapshot': getViewRegistrySnapshot,
41
42
  'ExtensionApi.renderViewInstance': renderViewInstance,
@@ -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.getViewActions': (uid: number) => Promise<readonly import("../View/View.ts").ViewAction[]>;
32
33
  'ExtensionApi.getViewMenuEntries': (uid: number, menuId: string) => Promise<readonly import("../View/View.ts").MenuEntry[]>;
33
34
  'ExtensionApi.getViewRegistrySnapshot': () => import("../View/View.ts").ViewRegistrySnapshot;
34
35
  'ExtensionApi.renderViewInstance': (uid: number) => Promise<import("../View/View.ts").ViewRenderResult>;
@@ -21,6 +21,11 @@ export interface MenuEntry {
21
21
  readonly id: string;
22
22
  readonly label: string;
23
23
  }
24
+ export interface ViewAction {
25
+ readonly command: string;
26
+ readonly icon: string;
27
+ readonly title: string;
28
+ }
24
29
  export interface DomEventListener {
25
30
  readonly capture?: boolean;
26
31
  readonly name: string | number;
@@ -35,6 +40,8 @@ export interface VirtualDomViewInstance {
35
40
  readonly getMenuEntries?: (menuId: string) => readonly MenuEntry[] | Promise<readonly MenuEntry[]>;
36
41
  readonly handleEvent?: (event: ViewEvent) => unknown;
37
42
  readonly render: () => readonly VirtualDomNode[] | Promise<readonly VirtualDomNode[]>;
43
+ readonly renderActions?: () => readonly ViewAction[] | Promise<readonly ViewAction[]>;
44
+ readonly renderFocus?: (oldContext: Readonly<Record<string, boolean>>, newContext: Readonly<Record<string, boolean>>) => string | Promise<string>;
38
45
  readonly saveState?: () => unknown;
39
46
  }
40
47
  export interface View {
@@ -61,9 +68,11 @@ export interface ViewRegistrySnapshot {
61
68
  }
62
69
  export interface ViewRenderResultDom {
63
70
  readonly dom: readonly VirtualDomNode[];
71
+ readonly focusSelector?: string;
64
72
  readonly type: 'setDom';
65
73
  }
66
74
  export interface ViewRenderResultPatches {
75
+ readonly focusSelector?: string;
67
76
  readonly patches: readonly unknown[];
68
77
  readonly type: 'setPatches';
69
78
  }
@@ -1,5 +1,5 @@
1
1
  import type { Disposable } from '../Disposable/Disposable.ts';
2
- import type { MenuEntry, View, ViewContext, ViewEvent, ViewRegistrySnapshot, ViewRenderResult } from '../View/View.ts';
2
+ import type { MenuEntry, View, ViewAction, ViewContext, ViewEvent, ViewRegistrySnapshot, ViewRenderResult } from '../View/View.ts';
3
3
  export declare const registerView: (view: View) => Disposable;
4
4
  export declare const executeViewProvider: (id: string) => unknown;
5
5
  export declare const createViewInstance: (viewId: string, uid: number, context?: ViewContext) => Promise<ViewRenderResult>;
@@ -8,5 +8,6 @@ export declare const renderViewInstance: (uid: number) => Promise<ViewRenderResu
8
8
  export declare const disposeViewInstance: (uid: number) => Promise<void>;
9
9
  export declare const saveViewInstanceState: (uid: number) => Promise<unknown>;
10
10
  export declare const getViewMenuEntries: (uid: number, menuId: string) => Promise<readonly MenuEntry[]>;
11
+ export declare const getViewActions: (uid: number) => Promise<readonly ViewAction[]>;
11
12
  export declare const getViewRegistrySnapshot: () => ViewRegistrySnapshot;
12
13
  export declare const resetViewRegistry: () => void;
@@ -147,6 +147,26 @@ const normalizeMenuEntries = (entries) => {
147
147
  }
148
148
  return entries.map(normalizeMenuEntry);
149
149
  };
150
+ const normalizeViewAction = (action, index) => {
151
+ if (!action || typeof action !== 'object' || Array.isArray(action)) {
152
+ throw new ExtensionApiError(`view action ${index} must be an object`);
153
+ }
154
+ const viewAction = action;
155
+ assertString(viewAction.title, `view action ${index} is missing title`);
156
+ assertString(viewAction.icon, `view action ${index} is missing icon`);
157
+ assertString(viewAction.command, `view action ${index} is missing command`);
158
+ return {
159
+ command: viewAction.command,
160
+ icon: viewAction.icon,
161
+ title: viewAction.title,
162
+ };
163
+ };
164
+ const normalizeViewActions = (actions) => {
165
+ if (!Array.isArray(actions)) {
166
+ throw new ExtensionApiError('view actions must be an array');
167
+ }
168
+ return actions.map(normalizeViewAction);
169
+ };
150
170
  const renderPatches = async (uid, instance) => {
151
171
  const oldDom = renderedDoms[uid] || [];
152
172
  const newDom = await renderDom(instance);
@@ -182,14 +202,23 @@ const isSameContext = (oldContext, newContext) => {
182
202
  }
183
203
  return true;
184
204
  };
205
+ const unchangedContext = {
206
+ changed: false,
207
+ newContext: {},
208
+ oldContext: {},
209
+ };
185
210
  const maybeNotifyContextChanged = async (uid, viewId, instance) => {
186
211
  if (typeof instance.getContext !== 'function') {
187
- return;
212
+ return unchangedContext;
188
213
  }
189
214
  const oldContext = contexts[uid] || {};
190
215
  const newContext = normalizeContext(instance.getContext());
191
216
  if (isSameContext(oldContext, newContext)) {
192
- return;
217
+ return {
218
+ changed: false,
219
+ newContext,
220
+ oldContext,
221
+ };
193
222
  }
194
223
  if (Object.keys(newContext).length === 0) {
195
224
  delete contexts[uid];
@@ -198,6 +227,31 @@ const maybeNotifyContextChanged = async (uid, viewId, instance) => {
198
227
  contexts[uid] = newContext;
199
228
  }
200
229
  await ExtensionManagementWorker.invoke('Extensions.handleViewContextChange', uid, viewId, newContext);
230
+ return {
231
+ changed: true,
232
+ newContext,
233
+ oldContext,
234
+ };
235
+ };
236
+ const renderFocus = async (instance, contextChange) => {
237
+ if (!contextChange.changed || typeof instance.renderFocus !== 'function') {
238
+ return '';
239
+ }
240
+ const focusSelector = await instance.renderFocus(contextChange.oldContext, contextChange.newContext);
241
+ if (typeof focusSelector !== 'string') {
242
+ throw new ExtensionApiError('view renderFocus result must be a string');
243
+ }
244
+ return focusSelector;
245
+ };
246
+ const withFocusSelector = async (result, instance, contextChange) => {
247
+ const focusSelector = await renderFocus(instance, contextChange);
248
+ if (!focusSelector) {
249
+ return result;
250
+ }
251
+ return {
252
+ ...result,
253
+ focusSelector,
254
+ };
201
255
  };
202
256
  const maybeClearContext = async (uid, viewId) => {
203
257
  if (!contexts[uid]) {
@@ -233,11 +287,12 @@ export const createViewInstance = async (viewId, uid, context) => {
233
287
  contextViewIds[uid] = viewId;
234
288
  const dom = await renderDom(instance);
235
289
  renderedDoms[uid] = dom;
236
- await maybeNotifyContextChanged(uid, viewId, instance);
237
- return {
290
+ const result = {
238
291
  dom,
239
292
  type: 'setDom',
240
293
  };
294
+ const contextChange = await maybeNotifyContextChanged(uid, viewId, instance);
295
+ return withFocusSelector(result, instance, contextChange);
241
296
  };
242
297
  export const dispatchViewEvent = async (uid, event) => {
243
298
  const instance = getVirtualDomInstance(uid);
@@ -245,14 +300,14 @@ export const dispatchViewEvent = async (uid, event) => {
245
300
  await instance.handleEvent(event);
246
301
  }
247
302
  const result = await renderPatches(uid, instance);
248
- await maybeNotifyContextChanged(uid, contextViewIds[uid], instance);
249
- return result;
303
+ const contextChange = await maybeNotifyContextChanged(uid, contextViewIds[uid], instance);
304
+ return withFocusSelector(result, instance, contextChange);
250
305
  };
251
306
  export const renderViewInstance = async (uid) => {
252
307
  const instance = getVirtualDomInstance(uid);
253
308
  const result = await renderPatches(uid, instance);
254
- await maybeNotifyContextChanged(uid, contextViewIds[uid], instance);
255
- return result;
309
+ const contextChange = await maybeNotifyContextChanged(uid, contextViewIds[uid], instance);
310
+ return withFocusSelector(result, instance, contextChange);
256
311
  };
257
312
  export const disposeViewInstance = async (uid) => {
258
313
  const instance = instances[uid];
@@ -279,6 +334,13 @@ export const getViewMenuEntries = async (uid, menuId) => {
279
334
  }
280
335
  return normalizeMenuEntries(await instance.getMenuEntries(menuId));
281
336
  };
337
+ export const getViewActions = async (uid) => {
338
+ const instance = getVirtualDomInstance(uid);
339
+ if (typeof instance.renderActions !== 'function') {
340
+ return [];
341
+ }
342
+ return normalizeViewActions(await instance.renderActions());
343
+ };
282
344
  export const getViewRegistrySnapshot = () => {
283
345
  return {
284
346
  views: Object.values(views).map(toRegisteredView),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/api",
3
- "version": "8.35.0",
3
+ "version": "8.37.0",
4
4
  "description": "Tree-shakeable extension API for Lvce Editor extensions.",
5
5
  "keywords": [
6
6
  "lvce-editor",