@workbench-kit/shell-react 0.0.2-prototype.0.2.26 → 0.0.2-prototype.0.2.27

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/README.md CHANGED
@@ -17,6 +17,7 @@ Peer: React 19.
17
17
  ## Quick start
18
18
 
19
19
  ```tsx
20
+ import { AppIcon } from '@workbench-kit/react/primitives';
20
21
  import { BUILTIN_WORKBENCH_EXTENSIONS } from '@workbench-kit/shell-react';
21
22
  import { EditorArea } from '@workbench-kit/shell-react';
22
23
  import { WorkbenchProvider } from '@workbench-kit/shell-react/provider';
@@ -25,7 +26,12 @@ import { WorkbenchShell } from '@workbench-kit/shell-react/shell';
25
26
  export function App() {
26
27
  return (
27
28
  <WorkbenchProvider availableExtensions={BUILTIN_WORKBENCH_EXTENSIONS}>
28
- <WorkbenchShell title="Workbench" primarySidebar={<aside />} editorArea={<EditorArea />} />
29
+ <WorkbenchShell
30
+ appIcon={<AppIcon alt="" src="/app-icon.svg" />}
31
+ title="Workbench"
32
+ primarySidebar={<aside />}
33
+ editorArea={<EditorArea />}
34
+ />
29
35
  </WorkbenchProvider>
30
36
  );
31
37
  }
@@ -40,7 +46,9 @@ host wants the Kit editor, or omit it for a product-owned editor region.
40
46
 
41
47
  Performance-sensitive hosts should import orchestration from the focused
42
48
  `provider`, `host-shell`, `shell`, `command-host`, `command-palette`, and
43
- `command-descriptors` subpaths. The root barrel remains the discovery surface,
49
+ `command-descriptors` subpaths. Hosts that already own the canonical extension
50
+ registry can use `registry-command-descriptors` without importing Provider
51
+ context into that leaf bundle. The root barrel remains the discovery surface,
44
52
  not the default runtime import graph.
45
53
 
46
54
  Use `host-shell` when the product owns sidebar, editor, panel, and overlay content.
@@ -53,6 +61,14 @@ layout-only chrome without host services, start from
53
61
  `@workbench-kit/react/workbench/shell` — see
54
62
  [Getting Started](../../docs/guides/getting-started.md).
55
63
 
64
+ ## Host-owned branding
65
+
66
+ The default title bar does not embed a product mark. Pass the host-owned mark through
67
+ `appIcon`, omit it for a text-only identity, or replace the complete `titleBar` surface.
68
+ The shell wraps `appIcon` as decorative content, so interactive controls do not belong
69
+ in this slot. `AppIcon` from `@workbench-kit/react/primitives` is the optional sizing
70
+ and image-fit helper; it does not include fallback artwork.
71
+
56
72
  ## Field Remap embed (slim subpath)
57
73
 
58
74
  Hosts that only need the Field Remap mapper UI can import the slim subpath instead
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.26",
3
+ "version": "0.0.2-prototype.0.2.27",
4
4
  "private": false,
5
5
  "sideEffects": [
6
6
  "**/*.css"
@@ -9,6 +9,7 @@
9
9
  "exports": {
10
10
  ".": "./src/index.ts",
11
11
  "./command-descriptors": "./src/commands/use-command-descriptors.ts",
12
+ "./registry-command-descriptors": "./src/commands/use-extension-registry-command-descriptors.ts",
12
13
  "./command-host": "./src/workbench/command-host.tsx",
13
14
  "./command-palette": "./src/workbench/command-palette.ts",
14
15
  "./field-remap": "./src/field-remap/index.ts",
@@ -30,15 +31,14 @@
30
31
  "@radix-ui/react-slot": "^1.3.0",
31
32
  "@xyflow/react": "^12.11.2",
32
33
  "jsonata": "^2.2.0",
33
- "react-table-mapping": "^1.0.3",
34
- "@workbench-kit/field-remap": "0.0.2-prototype.0.2.26",
35
- "@workbench-kit/tokens": "0.0.2-prototype.0.2.26",
36
- "@workbench-kit/workbench-config": "0.0.2-prototype.0.2.26",
37
- "@workbench-kit/workbench-extension-sdk": "0.0.2-prototype.0.2.26",
38
- "@workbench-kit/workbench-core": "0.0.2-prototype.0.2.26",
39
- "@workbench-kit/workspace": "0.0.2-prototype.0.2.26",
40
- "@workbench-kit/platform": "0.0.2-prototype.0.2.26",
41
- "@workbench-kit/react": "0.0.2-prototype.0.2.26"
34
+ "@workbench-kit/platform": "0.0.2-prototype.0.2.27",
35
+ "@workbench-kit/react": "0.0.2-prototype.0.2.27",
36
+ "@workbench-kit/tokens": "0.0.2-prototype.0.2.27",
37
+ "@workbench-kit/workbench-config": "0.0.2-prototype.0.2.27",
38
+ "@workbench-kit/field-remap": "0.0.2-prototype.0.2.27",
39
+ "@workbench-kit/workbench-extension-sdk": "0.0.2-prototype.0.2.27",
40
+ "@workbench-kit/workbench-core": "0.0.2-prototype.0.2.27",
41
+ "@workbench-kit/workspace": "0.0.2-prototype.0.2.27"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "react": "^19.0.0",
@@ -1,6 +1,6 @@
1
- import type {
2
- ResolveWorkbenchCommandExecutionPolicyInput,
3
- WorkbenchCommandExecutionPolicy,
1
+ import {
2
+ isWorkbenchCommandExecutionPolicy,
3
+ type ResolveWorkbenchCommandExecutionPolicyInput,
4
4
  } from '@workbench-kit/react/workbench';
5
5
  import type { WorkbenchStorageReader } from '@workbench-kit/workbench-core';
6
6
 
@@ -8,12 +8,6 @@ import { readPersistedLocalPreferences } from '../management/preference-settings
8
8
 
9
9
  export const WORKBENCH_AI_CHAT_COMMAND_DEFAULT_POLICY_KEY = 'workbench.chat.aiCommandDefaultPolicy';
10
10
 
11
- function isWorkbenchCommandExecutionPolicy(
12
- value: unknown,
13
- ): value is WorkbenchCommandExecutionPolicy {
14
- return value === 'auto-allow' || value === 'approval-required' || value === 'auto-deny';
15
- }
16
-
17
11
  export function readWorkbenchAiChatCommandPolicyInput(
18
12
  storageKey?: string,
19
13
  storage?: WorkbenchStorageReader,
@@ -29,14 +23,6 @@ export function readWorkbenchAiChatCommandPolicyInput(
29
23
  };
30
24
  }
31
25
 
32
- export function createWorkbenchAiChatCommandPolicyById(
33
- entries: Readonly<Record<string, WorkbenchCommandExecutionPolicy>>,
34
- ): ResolveWorkbenchCommandExecutionPolicyInput {
35
- return {
36
- policyByCommandId: entries,
37
- };
38
- }
39
-
40
26
  export function mergeWorkbenchAiChatCommandPolicyInput(
41
27
  ...inputs: readonly ResolveWorkbenchCommandExecutionPolicyInput[]
42
28
  ): ResolveWorkbenchCommandExecutionPolicyInput {
@@ -1,12 +1,7 @@
1
- import { useEffect, useMemo, useReducer } from 'react';
2
1
  import type { WorkbenchCommandDescriptor } from '@workbench-kit/react/workbench';
3
2
 
4
3
  import { useWorkbench } from '../shell/provider.js';
5
- import {
6
- collectExtensionCommandFeaturesById,
7
- extensionCommandToDescriptor,
8
- mergeWorkbenchCommandDescriptors,
9
- } from '../workbench/command-palette.js';
4
+ import { useExtensionRegistryCommandDescriptors } from './use-extension-registry-command-descriptors.js';
10
5
 
11
6
  const EMPTY_COMMAND_DESCRIPTORS: readonly WorkbenchCommandDescriptor[] = [];
12
7
 
@@ -14,28 +9,5 @@ export function useWorkbenchCommandDescriptors(
14
9
  additionalCommands: readonly WorkbenchCommandDescriptor[] = EMPTY_COMMAND_DESCRIPTORS,
15
10
  ) {
16
11
  const { extensionRegistry } = useWorkbench();
17
- const [refreshToken, refreshCommands] = useReducer((count: number) => count + 1, 0);
18
-
19
- useEffect(() => {
20
- const disposable = extensionRegistry.commands.onDidRegisterCommand(() => {
21
- refreshCommands();
22
- });
23
-
24
- return () => {
25
- disposable.dispose();
26
- };
27
- }, [extensionRegistry.commands]);
28
-
29
- return useMemo(() => {
30
- const commandFeaturesById = collectExtensionCommandFeaturesById(extensionRegistry);
31
-
32
- return mergeWorkbenchCommandDescriptors(
33
- extensionRegistry.commands
34
- .getCommands()
35
- .map((command) =>
36
- extensionCommandToDescriptor(command, commandFeaturesById.get(command.id)),
37
- ),
38
- [...additionalCommands],
39
- );
40
- }, [additionalCommands, extensionRegistry, refreshToken]);
12
+ return useExtensionRegistryCommandDescriptors(extensionRegistry, additionalCommands);
41
13
  }
@@ -1,18 +1,18 @@
1
- import { useEffect, useState } from 'react';
1
+ import { useCallback, useSyncExternalStore } from 'react';
2
2
  import type { ContextKeyService } from '@workbench-kit/platform';
3
3
 
4
4
  export function useContextKeyRevision(contextKeyService: ContextKeyService): number {
5
- const [revision, setRevision] = useState(0);
5
+ const subscribe = useCallback(
6
+ (onStoreChange: () => void) => {
7
+ const disposable = contextKeyService.onDidChangeContext(onStoreChange);
6
8
 
7
- useEffect(() => {
8
- const disposable = contextKeyService.onDidChangeContext(() => {
9
- setRevision((current) => current + 1);
10
- });
9
+ return () => {
10
+ disposable.dispose();
11
+ };
12
+ },
13
+ [contextKeyService],
14
+ );
15
+ const getSnapshot = useCallback(() => contextKeyService.getRevision(), [contextKeyService]);
11
16
 
12
- return () => {
13
- disposable.dispose();
14
- };
15
- }, [contextKeyService]);
16
-
17
- return revision;
17
+ return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
18
18
  }
@@ -0,0 +1,46 @@
1
+ import type { WorkbenchCommandDescriptor } from '@workbench-kit/react/workbench';
2
+ import type { ExtensionRegistry } from '@workbench-kit/workbench-core';
3
+ import { useEffect, useMemo, useReducer } from 'react';
4
+
5
+ import {
6
+ collectExtensionCommandFeaturesById,
7
+ extensionCommandToDescriptor,
8
+ mergeWorkbenchCommandDescriptors,
9
+ } from '../workbench/command-palette.js';
10
+
11
+ const EMPTY_COMMAND_DESCRIPTORS: readonly WorkbenchCommandDescriptor[] = [];
12
+
13
+ /**
14
+ * Resolves command descriptors from a host-owned registry without importing
15
+ * WorkbenchProvider. Use this leaf hook when the host already owns the
16
+ * canonical registry instance or when bundler context isolation matters.
17
+ */
18
+ export function useExtensionRegistryCommandDescriptors(
19
+ extensionRegistry: ExtensionRegistry,
20
+ additionalCommands: readonly WorkbenchCommandDescriptor[] = EMPTY_COMMAND_DESCRIPTORS,
21
+ ): readonly WorkbenchCommandDescriptor[] {
22
+ const [refreshToken, refreshCommands] = useReducer((count: number) => count + 1, 0);
23
+
24
+ useEffect(() => {
25
+ const disposable = extensionRegistry.commands.onDidChangeCommands(() => {
26
+ refreshCommands();
27
+ });
28
+
29
+ return () => {
30
+ disposable.dispose();
31
+ };
32
+ }, [extensionRegistry.commands]);
33
+
34
+ return useMemo(() => {
35
+ const commandFeaturesById = collectExtensionCommandFeaturesById(extensionRegistry);
36
+
37
+ return mergeWorkbenchCommandDescriptors(
38
+ extensionRegistry.commands
39
+ .getCommands()
40
+ .map((command) =>
41
+ extensionCommandToDescriptor(command, commandFeaturesById.get(command.id)),
42
+ ),
43
+ [...additionalCommands],
44
+ );
45
+ }, [additionalCommands, extensionRegistry, refreshToken]);
46
+ }
@@ -35,7 +35,7 @@ export function useWorkbenchDevtoolsSnapshot(): WorkbenchDevtoolsSnapshot {
35
35
  editorService.onDidChangeEditors(() => {
36
36
  notifyChange(onStoreChange);
37
37
  }),
38
- extensionRegistry.commands.onDidRegisterCommand(() => {
38
+ extensionRegistry.commands.onDidChangeCommands(() => {
39
39
  notifyChange(onStoreChange);
40
40
  }),
41
41
  extensionRegistry.menus.onDidRegisterMenuItem(() => {
@@ -71,29 +71,6 @@ export function countVisibleEditorPanes(visibility: EditorPaneVisibility): numbe
71
71
  return Number(visibility.code) + Number(visibility.form) + Number(visibility.preview);
72
72
  }
73
73
 
74
- export function toggleEditorPaneVisibility(
75
- visibility: EditorPaneVisibility,
76
- pane: EditorPaneKind,
77
- ): EditorPaneVisibility {
78
- const nextValue = !visibility[pane];
79
-
80
- if (!nextValue && countVisibleEditorPanes(visibility) <= 1) {
81
- return visibility;
82
- }
83
-
84
- return {
85
- ...visibility,
86
- [pane]: nextValue,
87
- };
88
- }
89
-
90
- export function sanitizeEditorPaneVisibility(
91
- visibility: EditorPaneVisibility,
92
- options: { formEligible: boolean; previewEligible: boolean },
93
- ): EditorPaneVisibility {
94
- return resolveEffectiveEditorPaneVisibility(visibility, options);
95
- }
96
-
97
74
  export function resolveEffectiveEditorPaneVisibility(
98
75
  preference: EditorPaneVisibility,
99
76
  options: { formEligible: boolean; previewEligible: boolean },
@@ -1,3 +1,4 @@
1
+ import { workspaceExplorerParentPaths } from '@workbench-kit/react/workbench/workspace';
1
2
  import type { WorkspaceSelectionState } from '@workbench-kit/workspace';
2
3
 
3
4
  import {
@@ -69,18 +70,6 @@ export function applyExplorerPathReveal(
69
70
  });
70
71
  }
71
72
 
72
- function workspaceExplorerParentPaths(path: string): string[] {
73
- const segments = path.split('/').filter(Boolean);
74
- return segments.slice(0, -1).map((_, index) => segments.slice(0, index + 1).join('/'));
75
- }
76
-
77
- export function isExplorerHostCommand(commandId: string): boolean {
78
- return (
79
- commandId === BUILTIN_EXPLORER_REVEAL_COMMAND_ID ||
80
- commandId === BUILTIN_EXPLORER_FOCUS_COMMAND_ID
81
- );
82
- }
83
-
84
73
  export async function runExplorerHostCommandSideEffects(
85
74
  commandId: string,
86
75
  args: readonly unknown[],
@@ -1,7 +1,6 @@
1
+ import { parseCommandInspectorUri } from '@workbench-kit/react/workbench/management';
1
2
  import type { EditorHost } from '@workbench-kit/workbench-extension-sdk';
2
3
 
3
- const COMMAND_INSPECTOR_URI_PREFIX = 'workbench://command/inspect/' as const;
4
-
5
4
  export const COMMAND_INSPECTOR_EDITOR_ID = 'workbench-kit.builtin.commands.inspector' as const;
6
5
  export const COMMAND_INSPECTOR_EDITOR_HOST_RENDER_KIND =
7
6
  'workbench-kit.builtin.commands/inspector' as const;
@@ -46,23 +45,6 @@ export class CommandInspectorEditorHost implements EditorHost {
46
45
  }
47
46
  }
48
47
 
49
- export function parseCommandInspectorUri(resourceUri: string): string | undefined {
50
- if (!resourceUri.startsWith(COMMAND_INSPECTOR_URI_PREFIX)) {
51
- return undefined;
52
- }
53
-
54
- const encodedId = resourceUri.slice(COMMAND_INSPECTOR_URI_PREFIX.length);
55
- if (!encodedId) {
56
- return undefined;
57
- }
58
-
59
- try {
60
- return decodeURIComponent(encodedId);
61
- } catch {
62
- return undefined;
63
- }
64
- }
65
-
66
48
  export function isCommandInspectorEditorHostRenderData(
67
49
  value: unknown,
68
50
  ): value is CommandInspectorEditorHostRenderData {
@@ -1,9 +1,9 @@
1
+ import { parseCommandInspectorUri } from '@workbench-kit/react/workbench/management';
1
2
  import type { ExtensionContext } from '@workbench-kit/workbench-extension-sdk';
2
3
 
3
4
  import {
4
5
  COMMAND_INSPECTOR_EDITOR_ID,
5
6
  CommandInspectorEditorHost,
6
- parseCommandInspectorUri,
7
7
  } from './command-inspector-editor-host.js';
8
8
 
9
9
  export const EXTENSION_ID = 'workbench-kit.builtin.commands' as const;
@@ -21,9 +21,9 @@ export {
21
21
  COMMAND_INSPECTOR_EDITOR_ID,
22
22
  CommandInspectorEditorHost,
23
23
  isCommandInspectorEditorHostRenderData,
24
- parseCommandInspectorUri,
25
24
  type CommandInspectorEditorHostRenderData,
26
25
  } from './command-inspector-editor-host.js';
26
+ export { parseCommandInspectorUri };
27
27
 
28
28
  export function activate(context: ExtensionContext): void {
29
29
  context.editorResolvers.registerResolver({
@@ -14,14 +14,6 @@ import {
14
14
  type ValueTransformRegistry,
15
15
  } from '@workbench-kit/field-remap';
16
16
 
17
- export type FieldRemapFlowNodeKind =
18
- | 'source-object'
19
- | 'target-object'
20
- | 'transform'
21
- | 'draft-transform'
22
- | 'combine-operator'
23
- | 'split-operator';
24
-
25
17
  export type FieldRemapPort = {
26
18
  fieldId: string;
27
19
  label: string;
@@ -106,14 +98,6 @@ const TRANSFORM_X = 320;
106
98
  const TARGET_X = 620;
107
99
  const TRANSFORM_ROW_GAP = 72;
108
100
 
109
- export function sourceObjectNodeId(): string {
110
- return SOURCE_OBJECT_NODE_ID;
111
- }
112
-
113
- export function targetObjectNodeId(): string {
114
- return TARGET_OBJECT_NODE_ID;
115
- }
116
-
117
101
  /** Handle id for a field/slot port on an object node. */
118
102
  export function portHandleId(fieldOrSlotId: string): string {
119
103
  return fieldOrSlotId;
@@ -7,7 +7,6 @@ import {
7
7
  findTargetSlot,
8
8
  isTransformChainCompatible,
9
9
  optionFieldsForStep,
10
- patchOptionStep,
11
10
  resizeOptionSteps,
12
11
  sanitizeOptionRecord,
13
12
  type CombineMappingOperator,
@@ -214,29 +213,6 @@ export function removeTransformStepFromEdge(edge: MappingEdge, stepIndex: number
214
213
  };
215
214
  }
216
215
 
217
- export function patchTransformStepOptionOnEdge(
218
- edge: MappingEdge,
219
- stepIndex: number,
220
- key: string,
221
- value: unknown,
222
- ): MappingEdge {
223
- const length = edge.transformIds?.length ?? 0;
224
- if (stepIndex < 0 || stepIndex >= length) {
225
- return edge;
226
- }
227
- return {
228
- ...edge,
229
- transformOptionSteps: patchOptionStep(
230
- edge.transformOptionSteps,
231
- length,
232
- stepIndex,
233
- key,
234
- value,
235
- edge.transformOptions,
236
- ),
237
- };
238
- }
239
-
240
216
  export function replaceTransformStepOptionsOnEdge(
241
217
  edge: MappingEdge,
242
218
  stepIndex: number,
package/src/index.ts CHANGED
@@ -242,6 +242,7 @@ export {
242
242
  type WorkbenchChatCommandSurfaceOptions,
243
243
  } from './chat/command-surface.js';
244
244
  export { useWorkbenchCommandDescriptors } from './commands/use-command-descriptors.js';
245
+ export { useExtensionRegistryCommandDescriptors } from './commands/use-extension-registry-command-descriptors.js';
245
246
  export {
246
247
  executeWorkbenchUserCommandAction,
247
248
  registerWorkbenchUserCommands,
@@ -1,4 +1,4 @@
1
- import { useCallback, useMemo } from 'react';
1
+ import { useMemo } from 'react';
2
2
  import { resolveWidgetStudioAssetCatalog } from '@workbench-kit/react/widget-studio';
3
3
  import { WidgetTreeLab } from '@workbench-kit/react/widget-tree';
4
4
  import { BUILTIN_JDW_REGISTRY } from '@workbench-kit/react/jdw';
@@ -6,7 +6,7 @@ import { BUILTIN_JDW_REGISTRY } from '@workbench-kit/react/jdw';
6
6
  import { useWorkbench } from '../shell/provider.js';
7
7
  import {
8
8
  isWorkspaceResourceService,
9
- useWorkspaceResourceState,
9
+ useWorkspaceTextDocuments,
10
10
  } from '../workbench/workspace-view-state.js';
11
11
 
12
12
  export interface JdwWidgetFormViewProps {
@@ -15,8 +15,6 @@ export interface JdwWidgetFormViewProps {
15
15
  readonly onContentChange: (content: string) => void;
16
16
  }
17
17
 
18
- const EMPTY_WORKSPACE_FILES: readonly { readonly path: string; readonly content: string }[] = [];
19
-
20
18
  /**
21
19
  * Form authoring surface for JDW widgets. Builds the Assets palette from built-in
22
20
  * assets, workspace asset packages, and other workspace `*.jdw.json` documents.
@@ -28,22 +26,7 @@ export function JdwWidgetFormView({ path, content, onContentChange }: JdwWidgetF
28
26
  const workspaceService = isWorkspaceResourceService(workspaceHostPort?.service)
29
27
  ? workspaceHostPort.service
30
28
  : undefined;
31
- const workspaceState = useWorkspaceResourceState(workspaceService);
32
- const files =
33
- workspaceState?.files ?? workspaceService?.getState().files ?? EMPTY_WORKSPACE_FILES;
34
-
35
- const filesByPath = useMemo(() => {
36
- const map = new Map<string, string>();
37
- for (const file of files) {
38
- map.set(file.path.replace(/\\/g, '/'), file.content);
39
- }
40
- return map;
41
- }, [files]);
42
-
43
- const loadDocument = useCallback(
44
- (documentPath: string) => filesByPath.get(documentPath.replace(/\\/g, '/')) ?? null,
45
- [filesByPath],
46
- );
29
+ const { files, loadDocument } = useWorkspaceTextDocuments(workspaceService);
47
30
 
48
31
  const assetCatalog = useMemo(
49
32
  () =>
@@ -1,11 +1,10 @@
1
- import { useCallback, useMemo } from 'react';
2
1
  import { JdwPreviewViewport } from '@workbench-kit/react/jdw/preview-viewport';
3
2
  import { BUILTIN_JDW_REGISTRY } from '@workbench-kit/react/jdw';
4
3
 
5
4
  import { useWorkbench } from '../shell/provider.js';
6
5
  import {
7
6
  isWorkspaceResourceService,
8
- useWorkspaceResourceState,
7
+ useWorkspaceTextDocuments,
9
8
  } from '../workbench/workspace-view-state.js';
10
9
 
11
10
  export interface JdwWidgetPreviewViewProps {
@@ -14,8 +13,6 @@ export interface JdwWidgetPreviewViewProps {
14
13
  readonly className?: string | undefined;
15
14
  }
16
15
 
17
- const EMPTY_WORKSPACE_FILES: readonly { readonly path: string; readonly content: string }[] = [];
18
-
19
16
  /**
20
17
  * Preview viewport for JDW documents. Expands workspace `type: "ref"` imports
21
18
  * before layout/draw without rewriting the source buffer.
@@ -25,22 +22,7 @@ export function JdwWidgetPreviewView({ path, content, className }: JdwWidgetPrev
25
22
  const workspaceService = isWorkspaceResourceService(workspaceHostPort?.service)
26
23
  ? workspaceHostPort.service
27
24
  : undefined;
28
- const workspaceState = useWorkspaceResourceState(workspaceService);
29
- const files =
30
- workspaceState?.files ?? workspaceService?.getState().files ?? EMPTY_WORKSPACE_FILES;
31
-
32
- const filesByPath = useMemo(() => {
33
- const map = new Map<string, string>();
34
- for (const file of files) {
35
- map.set(file.path.replace(/\\/g, '/'), file.content);
36
- }
37
- return map;
38
- }, [files]);
39
-
40
- const loadDocument = useCallback(
41
- (documentPath: string) => filesByPath.get(documentPath.replace(/\\/g, '/')) ?? null,
42
- [filesByPath],
43
- );
25
+ const { loadDocument } = useWorkspaceTextDocuments(workspaceService);
44
26
 
45
27
  return (
46
28
  <JdwPreviewViewport
@@ -7,6 +7,7 @@ import {
7
7
  } from '@workbench-kit/react/workbench/management';
8
8
  import { createWorkbenchShellCommands } from '@workbench-kit/react/workbench';
9
9
 
10
+ import { useContextKeyRevision } from '../commands/use-context-key-revision.js';
10
11
  import { useWorkbench } from '../shell/provider.js';
11
12
  import {
12
13
  collectExtensionCommandFeaturesById,
@@ -14,12 +15,17 @@ import {
14
15
  } from '../workbench/command-palette.js';
15
16
 
16
17
  export function useCommandManagementModel() {
17
- const { executeCommand, extensionRegistry } = useWorkbench();
18
+ const { contextKeyService, executeCommand, extensionRegistry } = useWorkbench();
19
+ const contextKeyRevision = useContextKeyRevision(contextKeyService);
20
+ const contextKeySnapshot = useMemo(
21
+ () => contextKeyService.createSnapshot(),
22
+ [contextKeyRevision, contextKeyService],
23
+ );
18
24
  const [lastRun, setLastRun] = useState<CommandManagementRunState | undefined>();
19
25
  const [refreshToken, refreshRegistry] = useReducer((count: number) => count + 1, 0);
20
26
 
21
27
  useEffect(() => {
22
- const commandDisposable = extensionRegistry.commands.onDidRegisterCommand(() => {
28
+ const commandDisposable = extensionRegistry.commands.onDidChangeCommands(() => {
23
29
  refreshRegistry();
24
30
  });
25
31
 
@@ -29,8 +35,8 @@ export function useCommandManagementModel() {
29
35
  }, [extensionRegistry]);
30
36
 
31
37
  const groups = useMemo(
32
- () => buildCommandManagementModelGroups(extensionRegistry, refreshToken),
33
- [extensionRegistry, refreshToken],
38
+ () => buildCommandManagementModelGroups(extensionRegistry, refreshToken, contextKeySnapshot),
39
+ [contextKeySnapshot, extensionRegistry, refreshToken],
34
40
  );
35
41
 
36
42
  const totalCount = countCommandManagementEntries(groups);
@@ -71,19 +77,28 @@ export function useCommandManagementModel() {
71
77
  export function buildCommandManagementModelGroups(
72
78
  extensionRegistry: ExtensionRegistry,
73
79
  _refreshToken = 0,
80
+ contextKeys?: object | undefined,
74
81
  ) {
75
- const shellCommands = createWorkbenchShellCommands({
82
+ const managedShellCommands = createWorkbenchShellCommands({
76
83
  activities: resolveShellCommandActivities(extensionRegistry),
77
84
  includeSettings: true,
78
85
  includeSidebarToggle: true,
79
86
  });
80
- const shellCommandIds = new Set(shellCommands.map((command) => command.id));
87
+ const managedShellCommandIds = new Set(managedShellCommands.map((command) => command.id));
88
+ const shellCommands =
89
+ contextKeys === undefined
90
+ ? managedShellCommands
91
+ : createWorkbenchShellCommands({
92
+ activities: resolveShellCommandActivities(extensionRegistry, contextKeys),
93
+ includeSettings: true,
94
+ includeSidebarToggle: true,
95
+ });
81
96
  const commandFeaturesById = collectExtensionCommandFeaturesById(extensionRegistry);
82
97
 
83
98
  return buildCommandManagementGroups({
84
99
  extensionCommands: collectExtensionCommandEntries(
85
100
  extensionRegistry,
86
- shellCommandIds,
101
+ managedShellCommandIds,
87
102
  commandFeaturesById,
88
103
  ),
89
104
  keybindingsByCommandId: collectKeybindingsByCommandId(extensionRegistry),
@@ -1,8 +1,5 @@
1
1
  import { useMemo } from 'react';
2
- import {
3
- buildKeybindingManagementEntries,
4
- type KeybindingDefinition,
5
- } from '@workbench-kit/platform';
2
+ import { buildKeybindingManagementEntries } from '@workbench-kit/platform';
6
3
  import { createWorkbenchShellCommands } from '@workbench-kit/react/workbench';
7
4
  import type { ExtensionRegistry } from '@workbench-kit/workbench-core';
8
5
 
@@ -122,9 +119,3 @@ function collectKeybindingManagementCommands(extensionRegistry: ExtensionRegistr
122
119
 
123
120
  return commands;
124
121
  }
125
-
126
- export function toKeybindingOverrideDefinitions(
127
- overrides: readonly KeybindingDefinition[],
128
- ): KeybindingDefinition[] {
129
- return overrides.map((binding) => ({ ...binding }));
130
- }
@@ -185,6 +185,24 @@ interface DeferredProviderDispose {
185
185
 
186
186
  const WorkbenchContext = createContext<WorkbenchContextValue | undefined>(undefined);
187
187
 
188
+ function createInitialContextKeyService(
189
+ contextKeyValues: Readonly<Record<string, ContextKeyValue>> | undefined,
190
+ ): ContextKeyService {
191
+ const service = new ContextKeyService();
192
+
193
+ for (const [key, value] of Object.entries(
194
+ createWorkbenchPermissionContextKeys({ role: 'owner' }),
195
+ )) {
196
+ service.set(key, value);
197
+ }
198
+
199
+ for (const [key, value] of Object.entries(contextKeyValues ?? {})) {
200
+ service.set(key, value);
201
+ }
202
+
203
+ return service;
204
+ }
205
+
188
206
  export function WorkbenchProvider({
189
207
  availableExtensions,
190
208
  children,
@@ -267,15 +285,7 @@ export function WorkbenchProvider({
267
285
  const [keybindingOverrides, setKeybindingOverridesState] = useState(
268
286
  resolvedInitialKeybindingOverrides,
269
287
  );
270
- const contextKeyService = useMemo(() => {
271
- const service = new ContextKeyService();
272
- for (const [key, value] of Object.entries(
273
- createWorkbenchPermissionContextKeys({ role: 'owner' }),
274
- )) {
275
- service.set(key, value);
276
- }
277
- return service;
278
- }, []);
288
+ const [contextKeyService] = useState(() => createInitialContextKeyService(contextKeyValues));
279
289
 
280
290
  useEffect(() => {
281
291
  return () => {