@workbench-kit/shell-react 0.0.2-prototype.0.2.33 → 0.0.2-prototype.0.2.34

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.
Files changed (51) hide show
  1. package/README.md +32 -0
  2. package/package.json +9 -9
  3. package/src/commands/use-command-descriptors.ts +7 -3
  4. package/src/commands/use-extension-registry-command-descriptors.ts +23 -6
  5. package/src/devtools/use-workbench-devtools-snapshot.ts +39 -14
  6. package/src/devtools/workbench-devtools-snapshot.ts +32 -13
  7. package/src/editor/area.tsx +3 -2
  8. package/src/editor/state-storage.ts +31 -0
  9. package/src/editor/tab-context-menu.ts +8 -8
  10. package/src/editor/use-editor.ts +6 -6
  11. package/src/explorer/context-menu.ts +8 -4
  12. package/src/explorer/view.tsx +4 -3
  13. package/src/extensions/canonical-extension-descriptions.ts +58 -0
  14. package/src/extensions/context-menu.ts +9 -6
  15. package/src/extensions/extension-enablement-context.ts +15 -0
  16. package/src/extensions/extension-enablement-controller.ts +453 -0
  17. package/src/extensions/management-model.ts +111 -47
  18. package/src/extensions/theme-selection-protection.ts +98 -0
  19. package/src/extensions/uninstall-eligibility.ts +103 -0
  20. package/src/extensions/use-extension-management.ts +151 -67
  21. package/src/extensions/view.tsx +4 -0
  22. package/src/field-remap/chrome-labels.ts +58 -2
  23. package/src/field-remap/convert-palette.tsx +151 -6
  24. package/src/field-remap/demo.tsx +31 -2
  25. package/src/field-remap/flow.tsx +57 -13
  26. package/src/field-remap/history.ts +99 -0
  27. package/src/field-remap/index.ts +9 -1
  28. package/src/field-remap/panel.tsx +371 -97
  29. package/src/field-remap/preview-controller.ts +122 -0
  30. package/src/field-remap/preview.tsx +171 -0
  31. package/src/field-remap/view.css +72 -0
  32. package/src/index.ts +16 -1
  33. package/src/management/keybinding-overrides-storage.ts +28 -0
  34. package/src/management/preference-settings-storage.ts +28 -0
  35. package/src/management/settings.tsx +2 -0
  36. package/src/management/use-command-management.ts +31 -26
  37. package/src/management/use-keybinding-management.ts +25 -12
  38. package/src/shell/focused-extension-services.ts +138 -0
  39. package/src/shell/host-shell.tsx +13 -10
  40. package/src/shell/persistence-diagnostic-context.ts +11 -0
  41. package/src/shell/provider.tsx +373 -69
  42. package/src/shell/settings.tsx +25 -16
  43. package/src/shell/shell.tsx +119 -78
  44. package/src/shell/view-host.tsx +23 -22
  45. package/src/storage/local-json-storage.ts +43 -29
  46. package/src/storage/persistence-diagnostics.ts +72 -0
  47. package/src/workbench/appearance-storage.ts +28 -0
  48. package/src/workbench/command-host.tsx +19 -22
  49. package/src/workbench/command-palette.ts +14 -13
  50. package/src/workbench/layout-storage.ts +52 -5
  51. package/src/workbench/use-persisted-appearance.ts +38 -12
package/README.md CHANGED
@@ -51,6 +51,23 @@ registry can use `registry-command-descriptors` without importing Provider
51
51
  context into that leaf bundle. The root barrel remains the discovery surface,
52
52
  not the default runtime import graph.
53
53
 
54
+ ### Focused extension context migration
55
+
56
+ `WorkbenchContextValue` no longer exposes the aggregate `ExtensionRegistry`.
57
+ Consumers that read Provider context use the focused `commands`, `menus`,
58
+ `extensionActivation`, `extensionActivationState`, `extensionCatalog`, and
59
+ `settingsCapabilityPublisher` fields instead. The focused contract types remain
60
+ available from the package root for external TypeScript consumers.
61
+
62
+ Hosts that explicitly own an `ExtensionRegistry` can continue passing that
63
+ instance to `useExtensionRegistryCommandDescriptors(registry, ...)`; this
64
+ host-composition hook is separate from Provider context and remains supported.
65
+
66
+ `WorkbenchShell` command hosts receive a focused `openSettings(categoryId?)`
67
+ control in their `onRunCommand` context. Use that host-composition seam for
68
+ commands that open a specific settings category without reading an aggregate
69
+ registry or capability map.
70
+
54
71
  Use `host-shell` when the product owns sidebar, editor, panel, and overlay content.
55
72
  It keeps Kit layout, Activity Bar ordering, resize persistence, and status routing
56
73
  without loading the full Settings/Profile/Help assembly. Use `shell` for the
@@ -94,6 +111,21 @@ import '@workbench-kit/shell-react/field-remap/view.css';
94
111
  for Flow-only embeds and custom bundler setups. The full barrel
95
112
  `import { FieldRemapPanel } from '@workbench-kit/shell-react'` stays supported.
96
113
 
114
+ ### Semantic history ownership
115
+
116
+ `FieldRemapPanel` keeps a private composite `{ edges, operators }` undo/redo stack only
117
+ when both durable channels are uncontrolled. If either channel is controlled, pass one
118
+ `historyOwner` for the complete composite state; the Panel never creates a partial stack.
119
+ `historyActionsRef` exposes host-chrome actions and
120
+ `onHistoryAvailabilityChange` reports whether those actions are available. Keyboard
121
+ routing remains host-owned.
122
+
123
+ Only semantic edits coming from the Flow mapper create entries. Hidden mappings are
124
+ reconstructed before an entry is recorded, so undo does not discard filtered state.
125
+ Shape apply or external source/target replacement prunes invalid mappings and resets
126
+ past/future without adding a history entry. Draft placement, selection, viewport, and
127
+ detail-panel state stay outside this history.
128
+
97
129
  ### Flow host chrome hooks
98
130
 
99
131
  `FieldRemapFlowMapper` (and `FieldRemapPanel` pass-through) accept optional chrome
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@workbench-kit/shell-react",
3
- "version": "0.0.2-prototype.0.2.33",
3
+ "version": "0.0.2-prototype.0.2.34",
4
4
  "private": false,
5
5
  "sideEffects": [
6
6
  "**/*.css"
@@ -31,14 +31,14 @@
31
31
  "@radix-ui/react-slot": "^1.3.0",
32
32
  "@xyflow/react": "^12.11.2",
33
33
  "jsonata": "^2.2.0",
34
- "@workbench-kit/react": "0.0.2-prototype.0.2.33",
35
- "@workbench-kit/field-remap": "0.0.2-prototype.0.2.33",
36
- "@workbench-kit/workbench-config": "0.0.2-prototype.0.2.33",
37
- "@workbench-kit/workbench-core": "0.0.2-prototype.0.2.33",
38
- "@workbench-kit/tokens": "0.0.2-prototype.0.2.33",
39
- "@workbench-kit/platform": "0.0.2-prototype.0.2.33",
40
- "@workbench-kit/workbench-extension-sdk": "0.0.2-prototype.0.2.33",
41
- "@workbench-kit/workspace": "0.0.2-prototype.0.2.33"
34
+ "@workbench-kit/platform": "0.0.2-prototype.0.2.34",
35
+ "@workbench-kit/field-remap": "0.0.2-prototype.0.2.34",
36
+ "@workbench-kit/workbench-core": "0.0.2-prototype.0.2.34",
37
+ "@workbench-kit/react": "0.0.2-prototype.0.2.34",
38
+ "@workbench-kit/workbench-config": "0.0.2-prototype.0.2.34",
39
+ "@workbench-kit/tokens": "0.0.2-prototype.0.2.34",
40
+ "@workbench-kit/workbench-extension-sdk": "0.0.2-prototype.0.2.34",
41
+ "@workbench-kit/workspace": "0.0.2-prototype.0.2.34"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "react": "^19.0.0",
@@ -1,13 +1,17 @@
1
1
  import type { WorkbenchCommandDescriptor } from '@workbench-kit/react/workbench';
2
2
 
3
3
  import { useWorkbench } from '../shell/provider.js';
4
- import { useExtensionRegistryCommandDescriptors } from './use-extension-registry-command-descriptors.js';
4
+ import { useCommandRegistryCommandDescriptors } from './use-extension-registry-command-descriptors.js';
5
5
 
6
6
  const EMPTY_COMMAND_DESCRIPTORS: readonly WorkbenchCommandDescriptor[] = [];
7
7
 
8
8
  export function useWorkbenchCommandDescriptors(
9
9
  additionalCommands: readonly WorkbenchCommandDescriptor[] = EMPTY_COMMAND_DESCRIPTORS,
10
10
  ) {
11
- const { extensionRegistry } = useWorkbench();
12
- return useExtensionRegistryCommandDescriptors(extensionRegistry, additionalCommands);
11
+ const { commands, extensionCatalog } = useWorkbench();
12
+ return useCommandRegistryCommandDescriptors(
13
+ commands,
14
+ extensionCatalog.getFeatureSpecs(),
15
+ additionalCommands,
16
+ );
13
17
  }
@@ -1,5 +1,6 @@
1
1
  import type { WorkbenchCommandDescriptor } from '@workbench-kit/react/workbench';
2
- import type { ExtensionRegistry } from '@workbench-kit/workbench-core';
2
+ import type { CommandRegistry } from '@workbench-kit/platform';
3
+ import type { ExtensionFeatureSpec, ExtensionRegistry } from '@workbench-kit/workbench-core';
3
4
  import { useEffect, useMemo, useReducer } from 'react';
4
5
 
5
6
  import {
@@ -18,29 +19,45 @@ const EMPTY_COMMAND_DESCRIPTORS: readonly WorkbenchCommandDescriptor[] = [];
18
19
  export function useExtensionRegistryCommandDescriptors(
19
20
  extensionRegistry: ExtensionRegistry,
20
21
  additionalCommands: readonly WorkbenchCommandDescriptor[] = EMPTY_COMMAND_DESCRIPTORS,
22
+ ): readonly WorkbenchCommandDescriptor[] {
23
+ return useCommandRegistryCommandDescriptors(
24
+ extensionRegistry.commands,
25
+ extensionRegistry.getFeatureSpecs(),
26
+ additionalCommands,
27
+ );
28
+ }
29
+
30
+ export function useCommandRegistryCommandDescriptors(
31
+ commands: CommandRegistry,
32
+ featureSpecs: readonly ExtensionFeatureSpec[],
33
+ additionalCommands: readonly WorkbenchCommandDescriptor[] = EMPTY_COMMAND_DESCRIPTORS,
21
34
  ): readonly WorkbenchCommandDescriptor[] {
22
35
  const [refreshToken, refreshCommands] = useReducer((count: number) => count + 1, 0);
23
36
 
24
37
  useEffect(() => {
25
- const disposable = extensionRegistry.commands.onDidChangeCommands(() => {
38
+ const disposable = commands.onDidChangeCommands(() => {
26
39
  refreshCommands();
27
40
  });
41
+ // A host may register commands in a layout effect after this hook rendered
42
+ // but before its passive subscription was installed. Re-read once after
43
+ // subscribing so that transition cannot leave the memoized snapshot stale.
44
+ refreshCommands();
28
45
 
29
46
  return () => {
30
47
  disposable.dispose();
31
48
  };
32
- }, [extensionRegistry.commands]);
49
+ }, [commands]);
33
50
 
34
51
  return useMemo(() => {
35
- const commandFeaturesById = collectExtensionCommandFeaturesById(extensionRegistry);
52
+ const commandFeaturesById = collectExtensionCommandFeaturesById(featureSpecs);
36
53
 
37
54
  return mergeWorkbenchCommandDescriptors(
38
- extensionRegistry.commands
55
+ commands
39
56
  .getCommands()
40
57
  .map((command) =>
41
58
  extensionCommandToDescriptor(command, commandFeaturesById.get(command.id)),
42
59
  ),
43
60
  [...additionalCommands],
44
61
  );
45
- }, [additionalCommands, extensionRegistry, refreshToken]);
62
+ }, [additionalCommands, commands, featureSpecs, refreshToken]);
46
63
  }
@@ -8,14 +8,31 @@ import { useWorkbench } from '../shell/provider.js';
8
8
  import type { WorkbenchWorkspaceHostPort as WorkspaceHostPort } from '@workbench-kit/workspace';
9
9
 
10
10
  export function useWorkbenchDevtoolsSnapshot(): WorkbenchDevtoolsSnapshot {
11
- const { editorService, extensionRegistry, layoutService, workspaceHostPort } = useWorkbench();
11
+ const {
12
+ activities,
13
+ commands,
14
+ editorService,
15
+ extensionActivationState,
16
+ extensionCatalog,
17
+ keybindings,
18
+ layoutService,
19
+ menus,
20
+ views,
21
+ workspaceHostPort,
22
+ } = useWorkbench();
12
23
 
13
24
  const store = useMemo(() => {
14
25
  const collectSnapshot = () =>
15
26
  collectWorkbenchDevtoolsSnapshot({
27
+ activities,
28
+ commands,
16
29
  editorService,
17
- extensionRegistry,
30
+ extensionActivationState,
31
+ extensionCatalog,
32
+ keybindings,
18
33
  layoutService,
34
+ menus,
35
+ views,
19
36
  workspaceHostPort,
20
37
  });
21
38
  let snapshot = collectSnapshot();
@@ -35,31 +52,28 @@ export function useWorkbenchDevtoolsSnapshot(): WorkbenchDevtoolsSnapshot {
35
52
  editorService.onDidChangeEditors(() => {
36
53
  notifyChange(onStoreChange);
37
54
  }),
38
- extensionRegistry.commands.onDidChangeCommands(() => {
55
+ commands.onDidChangeCommands(() => {
39
56
  notifyChange(onStoreChange);
40
57
  }),
41
- extensionRegistry.menus.onDidRegisterMenuItem(() => {
58
+ menus.onDidRegisterMenuItem(() => {
42
59
  notifyChange(onStoreChange);
43
60
  }),
44
- extensionRegistry.keybindings.onDidRegisterKeybinding(() => {
61
+ keybindings.onDidRegisterKeybinding(() => {
45
62
  notifyChange(onStoreChange);
46
63
  }),
47
- extensionRegistry.views.onDidRegisterView(() => {
64
+ views.onDidRegisterView(() => {
48
65
  notifyChange(onStoreChange);
49
66
  }),
50
- extensionRegistry.views.onDidRegisterViewContainer(() => {
67
+ views.onDidRegisterViewContainer(() => {
51
68
  notifyChange(onStoreChange);
52
69
  }),
53
- extensionRegistry.views.onDidRegisterViewProvider(() => {
70
+ views.onDidRegisterViewProvider(() => {
54
71
  notifyChange(onStoreChange);
55
72
  }),
56
- extensionRegistry.activities.onDidRegisterActivity(() => {
73
+ activities.onDidRegisterActivity(() => {
57
74
  notifyChange(onStoreChange);
58
75
  }),
59
- extensionRegistry.onDidActivateExtension(() => {
60
- notifyChange(onStoreChange);
61
- }),
62
- extensionRegistry.onDidDeactivateExtension(() => {
76
+ extensionActivationState.onDidChangeActiveExtensions(() => {
63
77
  notifyChange(onStoreChange);
64
78
  }),
65
79
  ];
@@ -78,7 +92,18 @@ export function useWorkbenchDevtoolsSnapshot(): WorkbenchDevtoolsSnapshot {
78
92
  };
79
93
  },
80
94
  };
81
- }, [editorService, extensionRegistry, layoutService, workspaceHostPort]);
95
+ }, [
96
+ activities,
97
+ commands,
98
+ editorService,
99
+ extensionActivationState,
100
+ extensionCatalog,
101
+ keybindings,
102
+ layoutService,
103
+ menus,
104
+ views,
105
+ workspaceHostPort,
106
+ ]);
82
107
 
83
108
  return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
84
109
  }
@@ -1,17 +1,24 @@
1
1
  import type {
2
2
  EditorService,
3
3
  EditorState,
4
+ ActivityRegistry,
4
5
  ExtensionDependencyDiagnostic,
5
- ExtensionRegistry,
6
6
  LayoutService,
7
+ MenuRegistry,
8
+ ViewRegistry,
7
9
  WorkbenchLayoutState,
8
10
  } from '@workbench-kit/workbench-core';
11
+ import type { CommandRegistry, KeybindingRegistry } from '@workbench-kit/platform';
9
12
  import type {
10
13
  WorkbenchWorkspaceHostPort as WorkspaceHostPort,
11
14
  WorkspaceResourceTransaction,
12
15
  } from '@workbench-kit/workspace';
13
16
 
14
- import type { WorkbenchWorkspaceHostPort } from '../shell/provider.js';
17
+ import type {
18
+ WorkbenchExtensionActivationStateReader,
19
+ WorkbenchExtensionCatalogReader,
20
+ WorkbenchWorkspaceHostPort,
21
+ } from '../shell/provider.js';
15
22
 
16
23
  export interface WorkbenchDevtoolsSnapshot {
17
24
  readonly activeExtensions: readonly { readonly extensionId: string }[];
@@ -58,18 +65,30 @@ export interface WorkbenchDevtoolsSnapshot {
58
65
  }
59
66
 
60
67
  export interface CollectWorkbenchDevtoolsSnapshotInput {
68
+ readonly activities: ActivityRegistry;
61
69
  readonly capturedAt?: string | undefined;
70
+ readonly commands: CommandRegistry;
62
71
  readonly editorService: EditorService;
63
- readonly extensionRegistry: ExtensionRegistry;
72
+ readonly extensionActivationState: WorkbenchExtensionActivationStateReader;
73
+ readonly extensionCatalog: WorkbenchExtensionCatalogReader;
74
+ readonly keybindings: KeybindingRegistry;
64
75
  readonly layoutService: LayoutService;
76
+ readonly menus: MenuRegistry;
77
+ readonly views: ViewRegistry;
65
78
  readonly workspaceHostPort?: WorkbenchWorkspaceHostPort | undefined;
66
79
  }
67
80
 
68
81
  export function collectWorkbenchDevtoolsSnapshot({
82
+ activities,
69
83
  capturedAt = new Date().toISOString(),
84
+ commands,
70
85
  editorService,
71
- extensionRegistry,
86
+ extensionActivationState,
87
+ extensionCatalog,
88
+ keybindings,
72
89
  layoutService,
90
+ menus,
91
+ views,
73
92
  workspaceHostPort,
74
93
  }: CollectWorkbenchDevtoolsSnapshotInput): WorkbenchDevtoolsSnapshot {
75
94
  const layout = layoutService.getState();
@@ -77,17 +96,17 @@ export function collectWorkbenchDevtoolsSnapshot({
77
96
  const activeTabCount = editor.groups.reduce((count, group) => count + group.tabs.length, 0);
78
97
 
79
98
  return {
80
- activeExtensions: extensionRegistry.getActiveExtensions().map(({ extensionId }) => ({
99
+ activeExtensions: extensionActivationState.getActiveExtensions().map(({ extensionId }) => ({
81
100
  extensionId,
82
101
  })),
83
- activities: extensionRegistry.activities.getActivities().map((activity) => ({
102
+ activities: activities.getActivities().map((activity) => ({
84
103
  icon: activity.icon,
85
104
  id: activity.id,
86
105
  title: activity.title,
87
106
  })),
88
- capabilities: extensionRegistry.capabilityRegistry.listProviderIds(),
107
+ capabilities: extensionCatalog.listCapabilityProviderIds(),
89
108
  capturedAt,
90
- commands: extensionRegistry.commands.getCommands().map((command) => ({
109
+ commands: commands.getCommands().map((command) => ({
91
110
  category: command.category,
92
111
  id: command.id,
93
112
  title: command.title,
@@ -102,15 +121,15 @@ export function collectWorkbenchDevtoolsSnapshot({
102
121
  'layout.sideBar.visible': layout.sideBar.visible,
103
122
  'workspace.hasHostPort': workspaceHostPort !== undefined,
104
123
  },
105
- dependencyDiagnostics: extensionRegistry.getDependencyDiagnostics(),
124
+ dependencyDiagnostics: extensionCatalog.getDependencyDiagnostics(),
106
125
  editor,
107
- keybindings: extensionRegistry.keybindings.getKeybindings().map((binding) => ({
126
+ keybindings: keybindings.getKeybindings().map((binding) => ({
108
127
  command: binding.command,
109
128
  key: binding.key,
110
129
  when: binding.when,
111
130
  })),
112
131
  layout,
113
- menus: extensionRegistry.menus.getMenuItems().map((menu) => ({
132
+ menus: menus.getMenuItems().map((menu) => ({
114
133
  command: menu.command,
115
134
  group: menu.group,
116
135
  menu: menu.menu,
@@ -118,13 +137,13 @@ export function collectWorkbenchDevtoolsSnapshot({
118
137
  when: menu.when,
119
138
  })),
120
139
  transactions: readWorkspaceTransactionJournal(workspaceHostPort),
121
- viewContainers: extensionRegistry.views.getViewContainers().map((container) => ({
140
+ viewContainers: views.getViewContainers().map((container) => ({
122
141
  icon: container.icon,
123
142
  id: container.id,
124
143
  location: container.location,
125
144
  title: container.title,
126
145
  })),
127
- views: extensionRegistry.views.getViews().map((view) => ({
146
+ views: views.getViews().map((view) => ({
128
147
  containerId: view.containerId,
129
148
  id: view.id,
130
149
  name: view.name,
@@ -265,7 +265,7 @@ function EditorGroupPane({
265
265
  viewProviders: readonly EditorDocumentViewProvider[];
266
266
  }) {
267
267
  const editorService = useEditorService();
268
- const { executeCommand, extensionRegistry } = useWorkbench();
268
+ const { commands, executeCommand, menus } = useWorkbench();
269
269
  const tabs = group.tabs;
270
270
  const activeTabId = group.activeTabId ?? tabs[0]?.id ?? '';
271
271
  const activeTab = tabs.find((tab) => tab.id === activeTabId) ?? tabs[0];
@@ -659,10 +659,11 @@ function EditorGroupPane({
659
659
  <ContextMenu
660
660
  ariaLabel="Editor tab menu"
661
661
  items={createEditorTabContextMenuItems({
662
+ commands,
662
663
  editorService,
663
664
  executeExtensionCommand: (commandId) => executeCommand(commandId),
664
- extensionRegistry,
665
665
  groupId: group.id,
666
+ menus,
666
667
  tab: contextTab,
667
668
  tabs,
668
669
  })}
@@ -4,6 +4,9 @@ import {
4
4
  type EditorLayoutNode,
5
5
  type EditorState,
6
6
  type EditorTabState,
7
+ type WorkbenchPersistenceDiagnosticOptions,
8
+ type WorkbenchPersistenceReadResult,
9
+ type WorkbenchPersistenceWriteResult,
7
10
  type WorkbenchStorageReader,
8
11
  type WorkbenchStorageWriter,
9
12
  } from '@workbench-kit/workbench-core';
@@ -11,8 +14,10 @@ import {
11
14
  import { isRecord } from '../is-record.js';
12
15
  import {
13
16
  readLocalJsonStorage,
17
+ readLocalJsonStorageResult,
14
18
  resolveLocalWorkbenchStorage,
15
19
  writeLocalJsonStorage,
20
+ writeLocalJsonStorageResult,
16
21
  } from '../storage/local-json-storage.js';
17
22
 
18
23
  export const DEFAULT_WORKBENCH_EDITOR_STATE_STORAGE_KEY = 'workbench-kit/.workbench/editors';
@@ -49,6 +54,20 @@ export function readPersistedEditorState(
49
54
  return readLocalJsonStorage(storageKey, parseEditorStateStorageValue, () => undefined, storage);
50
55
  }
51
56
 
57
+ export function readPersistedEditorStateResult(
58
+ storageKey = DEFAULT_WORKBENCH_EDITOR_STATE_STORAGE_KEY,
59
+ storage?: WorkbenchStorageReader,
60
+ options: WorkbenchPersistenceDiagnosticOptions = {},
61
+ ): WorkbenchPersistenceReadResult<EditorState | undefined> {
62
+ return readLocalJsonStorageResult(
63
+ storageKey,
64
+ parseEditorStateStorageValue,
65
+ () => undefined,
66
+ storage,
67
+ options,
68
+ );
69
+ }
70
+
52
71
  export function writePersistedEditorState(
53
72
  state: EditorState,
54
73
  storageKey = DEFAULT_WORKBENCH_EDITOR_STATE_STORAGE_KEY,
@@ -59,6 +78,18 @@ export function writePersistedEditorState(
59
78
  });
60
79
  }
61
80
 
81
+ export function writePersistedEditorStateResult(
82
+ state: EditorState,
83
+ storageKey = DEFAULT_WORKBENCH_EDITOR_STATE_STORAGE_KEY,
84
+ storage?: WorkbenchStorageWriter,
85
+ options: WorkbenchPersistenceDiagnosticOptions = {},
86
+ ): WorkbenchPersistenceWriteResult {
87
+ return writeLocalJsonStorageResult(storageKey, state, storage, {
88
+ ...options,
89
+ toStorageValue: editorStateToStorageValue,
90
+ });
91
+ }
92
+
62
93
  function parseEditorStateStorageValue(value: unknown): EditorState | undefined {
63
94
  if (!isRecord(value)) {
64
95
  return undefined;
@@ -12,11 +12,8 @@ import {
12
12
  createWorkbenchEditorTabMenuEntries,
13
13
  type WorkbenchEditorCommandContext,
14
14
  } from '@workbench-kit/react/workbench/commands';
15
- import type {
16
- EditorService,
17
- EditorTabState,
18
- ExtensionRegistry,
19
- } from '@workbench-kit/workbench-core';
15
+ import type { EditorService, EditorTabState, MenuRegistry } from '@workbench-kit/workbench-core';
16
+ import type { CommandRegistry } from '@workbench-kit/platform';
20
17
 
21
18
  import { copyResourcePath, pathForResource } from './resource.js';
22
19
  import {
@@ -34,17 +31,19 @@ const EDITOR_TAB_CONTEXT_MENU = 'editor/tab/context';
34
31
  const EDITOR_TAB_EXTENSION_MENU_SEPARATOR_ID = 'editor-tab-extension-separator';
35
32
 
36
33
  export function createEditorTabContextMenuItems({
34
+ commands,
37
35
  editorService,
38
36
  executeExtensionCommand,
39
- extensionRegistry,
40
37
  groupId,
38
+ menus,
41
39
  tab,
42
40
  tabs,
43
41
  }: {
42
+ commands?: CommandRegistry | undefined;
44
43
  editorService: EditorService;
45
44
  executeExtensionCommand?: ((commandId: string) => unknown) | undefined;
46
- extensionRegistry?: ExtensionRegistry | undefined;
47
45
  groupId: string;
46
+ menus?: MenuRegistry | undefined;
48
47
  tab: EditorTabState;
49
48
  tabs: readonly EditorTabState[];
50
49
  }): ContextMenuItem[] {
@@ -108,10 +107,11 @@ export function createEditorTabContextMenuItems({
108
107
  return appendExtensionContextMenuItems(
109
108
  baseItems,
110
109
  createExtensionContextMenuItems({
110
+ commands,
111
111
  contextKeys,
112
112
  executeCommand: executeExtensionCommand,
113
- extensionRegistry,
114
113
  menu: EDITOR_TAB_CONTEXT_MENU,
114
+ menus,
115
115
  }),
116
116
  EDITOR_TAB_EXTENSION_MENU_SEPARATOR_ID,
117
117
  );
@@ -38,7 +38,7 @@ export function useActiveEditorTab(): EditorTabState | undefined {
38
38
 
39
39
  export function useEditorHost(tabId?: string): EditorHost | undefined {
40
40
  const editorService = useEditorService();
41
- const { extensionRegistry, waitForExtensionStartup } = useWorkbench();
41
+ const { extensionActivation, waitForExtensionStartup } = useWorkbench();
42
42
  const editorState = useEditorState();
43
43
  const forceRender = useForceRender();
44
44
  const activeGroup = editorState.groups.find((group) => group.id === editorState.activeGroupId);
@@ -68,11 +68,11 @@ export function useEditorHost(tabId?: string): EditorHost | undefined {
68
68
  useEffect(() => {
69
69
  // Late onView / onCommand activations register editor host factories after the
70
70
  // first paint that restores persisted tabs. Retry host creation when they land.
71
- const disposable = extensionRegistry.onDidActivateExtension(forceRender);
71
+ const disposable = extensionActivation.onDidActivateExtension(forceRender);
72
72
  return () => {
73
73
  disposable.dispose();
74
74
  };
75
- }, [extensionRegistry, forceRender]);
75
+ }, [extensionActivation, forceRender]);
76
76
 
77
77
  if (!resolvedTabId) {
78
78
  return undefined;
@@ -87,7 +87,7 @@ export function useEditorHost(tabId?: string): EditorHost | undefined {
87
87
  export function useEditorDocumentViewProviders(
88
88
  localProviders?: readonly EditorDocumentViewProvider[] | undefined,
89
89
  ): readonly EditorDocumentViewProvider[] {
90
- const { editorDocumentViewProviders, extensionRegistry, waitForExtensionStartup } =
90
+ const { editorDocumentViewProviders, extensionActivation, waitForExtensionStartup } =
91
91
  useWorkbench();
92
92
  const forceRender = useForceRender();
93
93
 
@@ -115,11 +115,11 @@ export function useEditorDocumentViewProviders(
115
115
  }, [forceRender, waitForExtensionStartup]);
116
116
 
117
117
  useEffect(() => {
118
- const disposable = extensionRegistry.onDidActivateExtension(forceRender);
118
+ const disposable = extensionActivation.onDidActivateExtension(forceRender);
119
119
  return () => {
120
120
  disposable.dispose();
121
121
  };
122
- }, [extensionRegistry, forceRender]);
122
+ }, [extensionActivation, forceRender]);
123
123
 
124
124
  const registryProviders = editorDocumentViewProviders.getProviders();
125
125
 
@@ -8,12 +8,13 @@ import {
8
8
  type WorkbenchWorkspaceCommandContext,
9
9
  } from '@workbench-kit/react/workbench/commands';
10
10
  import {
11
+ type CommandRegistry,
11
12
  createCommandRegistry,
12
13
  executeCommand as executeRegisteredCommand,
13
14
  resolveCommandMenuItems,
14
15
  } from '@workbench-kit/platform';
15
16
  import { parentPathOf, type WorkspaceTreeNode } from '@workbench-kit/workspace';
16
- import type { ExtensionRegistry } from '@workbench-kit/workbench-core';
17
+ import type { MenuRegistry } from '@workbench-kit/workbench-core';
17
18
  import {
18
19
  appendExtensionContextMenuItems,
19
20
  createExtensionContextMenuItems,
@@ -29,26 +30,28 @@ const EXPLORER_EXTENSION_MENU_SEPARATOR_ID = 'explorer-extension-separator';
29
30
 
30
31
  export function createExplorerItemContextMenuItems({
31
32
  actionPaths,
33
+ commands,
32
34
  copyPaths,
33
35
  createFile,
34
36
  createFolder,
35
37
  deleteTargets,
36
38
  executeExtensionCommand,
37
- extensionRegistry,
38
39
  files,
40
+ menus,
39
41
  node,
40
42
  openFiles,
41
43
  revealFolder,
42
44
  renameTarget,
43
45
  }: {
44
46
  actionPaths: readonly string[];
47
+ commands?: CommandRegistry | undefined;
45
48
  copyPaths: (paths: string[]) => void;
46
49
  createFile: (parentPath: string) => void;
47
50
  createFolder: (parentPath: string) => void;
48
51
  deleteTargets: (paths: string[]) => void;
49
52
  executeExtensionCommand?: ((commandId: string) => unknown) | undefined;
50
- extensionRegistry?: ExtensionRegistry | undefined;
51
53
  files: readonly { path: string }[];
54
+ menus?: MenuRegistry | undefined;
52
55
  node: WorkspaceTreeNode;
53
56
  openFiles: (paths: string[]) => void;
54
57
  revealFolder: (path: string) => void;
@@ -104,10 +107,11 @@ export function createExplorerItemContextMenuItems({
104
107
  return appendExtensionContextMenuItems(
105
108
  baseItems,
106
109
  createExtensionContextMenuItems({
110
+ commands,
107
111
  contextKeys,
108
112
  executeCommand: executeExtensionCommand,
109
- extensionRegistry,
110
113
  menu: EXPLORER_CONTEXT_MENU,
114
+ menus,
111
115
  }),
112
116
  EXPLORER_EXTENSION_MENU_SEPARATOR_ID,
113
117
  );
@@ -52,7 +52,7 @@ interface ExplorerContextMenuState {
52
52
  }
53
53
 
54
54
  export function BuiltinExplorerView() {
55
- const { executeCommand, extensionRegistry, workspaceHostPort } = useWorkbench();
55
+ const { commands, executeCommand, menus, workspaceHostPort } = useWorkbench();
56
56
  const activeTab = useActiveEditorTab();
57
57
  const workspaceService = isWorkspaceResourceService(workspaceHostPort?.service)
58
58
  ? workspaceHostPort.service
@@ -146,6 +146,7 @@ export function BuiltinExplorerView() {
146
146
  ariaLabel: `${node.name} menu`,
147
147
  items: createExplorerItemContextMenuItems({
148
148
  actionPaths: meta.actionPaths,
149
+ commands,
149
150
  copyPaths: (paths) => {
150
151
  void executeWorkspaceCommand(WORKBENCH_WORKSPACE_COPY_PATH_COMMAND_ID, { paths });
151
152
  },
@@ -158,8 +159,8 @@ export function BuiltinExplorerView() {
158
159
  });
159
160
  },
160
161
  executeExtensionCommand: (commandId) => executeCommand(commandId),
161
- extensionRegistry,
162
162
  files: workspaceState?.files ?? [],
163
+ menus,
163
164
  node,
164
165
  openFiles: (paths) => {
165
166
  void executeWorkspaceCommand(WORKBENCH_WORKSPACE_OPEN_COMMAND_ID, {
@@ -174,7 +175,7 @@ export function BuiltinExplorerView() {
174
175
  y: event.clientY,
175
176
  });
176
177
  },
177
- [executeWorkspaceCommand, explorer, workspaceState?.files],
178
+ [commands, executeWorkspaceCommand, explorer, menus, workspaceState?.files],
178
179
  );
179
180
 
180
181
  if (!workspaceService || !workspaceState) {