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

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 (35) hide show
  1. package/README.md +18 -2
  2. package/package.json +10 -10
  3. package/src/chat/command-policy.ts +3 -17
  4. package/src/commands/use-command-descriptors.ts +2 -30
  5. package/src/commands/use-context-key-revision.ts +12 -12
  6. package/src/commands/use-extension-registry-command-descriptors.ts +46 -0
  7. package/src/devtools/use-workbench-devtools-snapshot.ts +1 -1
  8. package/src/editor/pane-visibility.ts +0 -23
  9. package/src/editor/view-providers.tsx +0 -7
  10. package/src/explorer/reveal.ts +1 -12
  11. package/src/extensions/builtin/commands/src/command-inspector-editor-host.ts +1 -19
  12. package/src/extensions/builtin/commands/src/index.ts +2 -2
  13. package/src/field-remap/convert-note-editor.tsx +2 -4
  14. package/src/field-remap/convert-palette.tsx +17 -20
  15. package/src/field-remap/flow-adapter.ts +21 -57
  16. package/src/field-remap/flow-ops.ts +0 -24
  17. package/src/field-remap/index.ts +0 -1
  18. package/src/field-remap/io-class-browse.tsx +29 -6
  19. package/src/field-remap/jsonata-transform.ts +9 -39
  20. package/src/field-remap/shape-io-editor.tsx +1 -1
  21. package/src/field-remap/transform-options-editor.tsx +1 -2
  22. package/src/field-remap/view.css +4 -29
  23. package/src/index.ts +1 -10
  24. package/src/jdw/widget-form-view.tsx +3 -20
  25. package/src/jdw/widget-preview-view.tsx +2 -20
  26. package/src/management/use-command-management.ts +22 -7
  27. package/src/management/use-keybinding-management.ts +1 -10
  28. package/src/shell/provider.tsx +19 -9
  29. package/src/shell/shell.tsx +11 -4
  30. package/src/workbench/command-host.tsx +46 -7
  31. package/src/workbench/command-palette.ts +12 -45
  32. package/src/workbench/workspace-view-state.ts +18 -1
  33. package/src/field-remap/graph.tsx +0 -559
  34. package/src/field-remap/table-mapping-adapter.ts +0 -59
  35. package/src/field-remap/tree.tsx +0 -404
@@ -20,6 +20,10 @@ export interface FieldRemapIoClassBrowseProps {
20
20
  readonly targetsTitle?: string;
21
21
  readonly className?: string;
22
22
  readonly emptyLabel?: string;
23
+ readonly labels?: {
24
+ readonly hiddenBadge?: string;
25
+ readonly classRefTitle?: string;
26
+ };
23
27
  }
24
28
 
25
29
  export function resolveFieldRemapIoChrome(
@@ -39,9 +43,11 @@ function formatClassRef(classRef: { readonly id: string; readonly version: numbe
39
43
  function FieldTree({
40
44
  nodes,
41
45
  emptyLabel,
46
+ labels,
42
47
  }: {
43
48
  readonly nodes: readonly (SourceField | TargetSlot)[];
44
49
  readonly emptyLabel: string;
50
+ readonly labels: FieldRemapIoClassBrowseProps['labels'];
45
51
  }): JSX.Element {
46
52
  if (nodes.length === 0) {
47
53
  return <p className="workbench-field-remap-io-browse__empty">{emptyLabel}</p>;
@@ -55,19 +61,22 @@ function FieldTree({
55
61
  <span className="workbench-field-remap-io-browse__meta">
56
62
  {node.dataType ? <span>{node.dataType}</span> : null}
57
63
  {node.classRef ? (
58
- <span className="workbench-field-remap-io-browse__badge" title="classRef">
64
+ <span
65
+ className="workbench-field-remap-io-browse__badge"
66
+ title={labels?.classRefTitle ?? 'classRef'}
67
+ >
59
68
  {formatClassRef(node.classRef)}
60
69
  </span>
61
70
  ) : null}
62
71
  {node.hidden === true ? (
63
72
  <span className="workbench-field-remap-io-browse__badge" title="hidden">
64
- Hidden
73
+ {labels?.hiddenBadge ?? 'Hidden'}
65
74
  </span>
66
75
  ) : null}
67
76
  </span>
68
77
  </div>
69
78
  {node.children?.length ? (
70
- <FieldTree emptyLabel={emptyLabel} nodes={node.children} />
79
+ <FieldTree emptyLabel={emptyLabel} labels={labels} nodes={node.children} />
71
80
  ) : null}
72
81
  </li>
73
82
  ))}
@@ -79,10 +88,12 @@ function PortSection({
79
88
  title,
80
89
  nodes,
81
90
  emptyLabel,
91
+ labels,
82
92
  }: {
83
93
  readonly title: string;
84
94
  readonly nodes: readonly (SourceField | TargetSlot)[];
85
95
  readonly emptyLabel: string;
96
+ readonly labels: FieldRemapIoClassBrowseProps['labels'];
86
97
  }): JSX.Element {
87
98
  return (
88
99
  <section aria-label={title} className="workbench-field-remap-io-browse__section">
@@ -96,11 +107,12 @@ function PortSection({
96
107
  <span className="workbench-field-remap-io-browse__port-id">{node.label}</span>
97
108
  <span className="workbench-field-remap-io-browse__port-meta">
98
109
  {node.classRef ? formatClassRef(node.classRef) : node.id}
99
- {node.hidden === true ? ' · Hidden' : ''}
110
+ {node.hidden === true ? ` · ${labels?.hiddenBadge ?? 'Hidden'}` : ''}
100
111
  </span>
101
112
  </p>
102
113
  <FieldTree
103
114
  emptyLabel={emptyLabel}
115
+ labels={labels}
104
116
  nodes={node.children?.length ? node.children : [node]}
105
117
  />
106
118
  </div>
@@ -123,6 +135,7 @@ export function FieldRemapIoClassBrowse({
123
135
  targetsTitle = 'Outputs',
124
136
  className,
125
137
  emptyLabel = 'No fields',
138
+ labels,
126
139
  }: FieldRemapIoClassBrowseProps): JSX.Element {
127
140
  const projectedSources = useMemo(
128
141
  () => projectSourceFields(sources, { includeHidden }),
@@ -138,8 +151,18 @@ export function FieldRemapIoClassBrowse({
138
151
  className={['workbench-field-remap-io-browse', className].filter(Boolean).join(' ')}
139
152
  data-testid="field-remap-io-browse"
140
153
  >
141
- <PortSection emptyLabel={emptyLabel} nodes={projectedSources} title={sourcesTitle} />
142
- <PortSection emptyLabel={emptyLabel} nodes={projectedTargets} title={targetsTitle} />
154
+ <PortSection
155
+ emptyLabel={emptyLabel}
156
+ labels={labels}
157
+ nodes={projectedSources}
158
+ title={sourcesTitle}
159
+ />
160
+ <PortSection
161
+ emptyLabel={emptyLabel}
162
+ labels={labels}
163
+ nodes={projectedTargets}
164
+ title={targetsTitle}
165
+ />
143
166
  </div>
144
167
  );
145
168
  }
@@ -10,8 +10,6 @@ export const DEFAULT_JSONATA_TIMEOUT_MS = 2_000;
10
10
  /** Default maximum expression source length (characters). */
11
11
  export const DEFAULT_JSONATA_MAX_EXPRESSION_LENGTH = 4_096;
12
12
 
13
- export type JsonataTransformErrorPolicy = 'passthrough' | 'throw';
14
-
15
13
  export interface CreateJsonataValueTransformOptions {
16
14
  /** Wall-clock timeout for `evaluate` (default {@link DEFAULT_JSONATA_TIMEOUT_MS}). */
17
15
  readonly timeoutMs?: number;
@@ -20,11 +18,6 @@ export interface CreateJsonataValueTransformOptions {
20
18
  * (default {@link DEFAULT_JSONATA_MAX_EXPRESSION_LENGTH}).
21
19
  */
22
20
  readonly maxExpressionLength?: number;
23
- /**
24
- * - `throw` (default): surface timeout / compile / evaluate failures to the host
25
- * - `passthrough`: return the original value (legacy silent identity)
26
- */
27
- readonly onError?: JsonataTransformErrorPolicy;
28
21
  }
29
22
 
30
23
  export function createJsonataValueTransform(
@@ -32,7 +25,6 @@ export function createJsonataValueTransform(
32
25
  ): ValueTransformDefinition {
33
26
  const timeoutMs = options.timeoutMs ?? DEFAULT_JSONATA_TIMEOUT_MS;
34
27
  const maxExpressionLength = options.maxExpressionLength ?? DEFAULT_JSONATA_MAX_EXPRESSION_LENGTH;
35
- const onError = options.onError ?? 'throw';
36
28
 
37
29
  return {
38
30
  id: JSONATA_TRANSFORM_ID,
@@ -57,30 +49,22 @@ export function createJsonataValueTransform(
57
49
  }
58
50
 
59
51
  if (expression.length > maxExpressionLength) {
60
- return handleJsonataError(
61
- new Error(
62
- `JSONata expression exceeds max length (${expression.length} > ${maxExpressionLength}).`,
63
- ),
64
- value,
65
- onError,
52
+ throw new Error(
53
+ `JSONata expression exceeds max length (${expression.length} > ${maxExpressionLength}).`,
66
54
  );
67
55
  }
68
56
 
69
- try {
70
- const compiled = jsonata(expression);
71
- // jsonata@2.x evaluate returns a Promise (1.x was synchronous).
72
- return await raceJsonataEvaluation(compiled.evaluate(value), {
73
- timeoutMs,
74
- signal: context.signal,
75
- });
76
- } catch (error) {
77
- return handleJsonataError(error, value, onError);
78
- }
57
+ const compiled = jsonata(expression);
58
+ // jsonata@2.x evaluate returns a Promise (1.x was synchronous).
59
+ return await raceJsonataEvaluation(compiled.evaluate(value), {
60
+ timeoutMs,
61
+ signal: context.signal,
62
+ });
79
63
  },
80
64
  };
81
65
  }
82
66
 
83
- /** Default bounded transform (`onError: 'throw'`, 2s timeout, 4k expression cap). */
67
+ /** Default bounded transform (2s timeout, 4k expression cap). */
84
68
  export const jsonataValueTransform: ValueTransformDefinition = createJsonataValueTransform();
85
69
 
86
70
  export class JsonataTransformTimeoutError extends Error {
@@ -150,17 +134,3 @@ export async function raceJsonataEvaluation<T>(
150
134
  );
151
135
  });
152
136
  }
153
-
154
- function handleJsonataError(
155
- error: unknown,
156
- value: unknown,
157
- onError: JsonataTransformErrorPolicy,
158
- ): unknown {
159
- if (onError === 'passthrough') {
160
- return value;
161
- }
162
- if (error instanceof Error) {
163
- throw error;
164
- }
165
- throw new Error(String(error));
166
- }
@@ -34,7 +34,7 @@ function formatJson(value: unknown): string {
34
34
 
35
35
  /**
36
36
  * Host-owned shape IO surface: paste JSON → ingest fields/slots, edit FieldDataType.
37
- * Persistence stays host-owned (`FieldRemapDocument` v1 is edges-only).
37
+ * Persistence stays host-owned (`FieldRemapDocument` stores mappings, not shapes).
38
38
  */
39
39
  export function FieldRemapShapeIoEditor({
40
40
  role,
@@ -195,8 +195,7 @@ function JsonOptionEditor({
195
195
 
196
196
  /**
197
197
  * Presentational editor for `TransformOptionField` kinds (string / number / boolean /
198
- * stringMap / json). Emits a full options record; callers typically pass through
199
- * `patchOptionStep` / `sanitizeOptionRecord`.
198
+ * stringMap / json). Emits the full options record for one transform step.
200
199
  */
201
200
  export function TransformOptionsEditor({
202
201
  fields,
@@ -365,41 +365,16 @@
365
365
  }
366
366
 
367
367
  .workbench-field-remap-convert-palette__list {
368
- list-style: none;
369
- margin: 0;
370
- padding: 0;
371
- display: flex;
372
368
  flex: 1 1 auto;
373
- flex-direction: column;
374
- gap: 0.35rem;
375
369
  min-height: 0;
376
370
  overflow: auto;
377
371
  }
378
372
 
379
- .workbench-field-remap-convert-palette__item {
380
- display: flex;
381
- flex-direction: column;
382
- align-items: flex-start;
383
- gap: 0.15rem;
384
- width: 100%;
385
- margin: 0;
386
- padding: 0.45rem 0.5rem;
387
- border: 1px solid var(--vscode-panel-border, var(--color-border));
388
- border-radius: 0.25rem;
389
- background: var(--vscode-editor-background, var(--color-bg));
390
- color: inherit;
391
- text-align: left;
392
- cursor: pointer;
393
- font: inherit;
394
- }
395
-
396
- .workbench-field-remap-convert-palette__item.is-selected {
397
- border-color: var(--vscode-focusBorder, var(--color-accent, #3794ff));
398
- }
399
-
400
- .workbench-field-remap-convert-palette__item code {
373
+ .workbench-field-remap-convert-palette__list code {
374
+ overflow: hidden;
375
+ text-overflow: ellipsis;
376
+ white-space: nowrap;
401
377
  font-size: 0.7rem;
402
- word-break: break-all;
403
378
  }
404
379
 
405
380
  .workbench-field-remap-convert-palette__operators {
package/src/index.ts CHANGED
@@ -63,11 +63,8 @@ export { EditorArea, type EditorAreaProps, type EditorViewMode } from './editor/
63
63
  export {
64
64
  DEFAULT_EDITOR_DOCUMENT_VIEW_PROVIDERS,
65
65
  EditorDocumentViewProviderRegistry,
66
- JDW_PREVIEW_PROVIDER_ID,
67
- JDW_WIDGET_FORM_PROVIDER_ID,
68
66
  JSON_FORM_PROVIDER,
69
67
  JSON_FORM_PROVIDER_ID,
70
- MARKDOWN_PREVIEW_PROVIDER_ID,
71
68
  createEditorDocumentViewProviderRegistry,
72
69
  resolveEditorDocumentViewProvider,
73
70
  resolveEditorDocumentViews,
@@ -182,7 +179,6 @@ export {
182
179
  createJsonataValueTransform,
183
180
  jsonataValueTransform,
184
181
  type CreateJsonataValueTransformOptions,
185
- type JsonataTransformErrorPolicy,
186
182
  } from './field-remap/jsonata-transform.js';
187
183
  export {
188
184
  FIELD_REMAP_SAMPLES,
@@ -216,13 +212,7 @@ export {
216
212
  type WorkbenchThemeOption,
217
213
  } from './shell/shell.js';
218
214
  export {
219
- getWorkbenchCommandPaletteShortcutLabel,
220
- getWorkbenchQuickAccessShortcutLabel,
221
- WORKBENCH_COMMAND_PALETTE_SHORTCUT,
222
- WORKBENCH_QUICK_ACCESS_SHORTCUT,
223
215
  buildWorkbenchPaletteCommands,
224
- matchesWorkbenchCommandPaletteShortcut,
225
- matchesWorkbenchQuickAccessShortcut,
226
216
  mergeWorkbenchCommandDescriptors,
227
217
  resolveShellCommandActivities,
228
218
  } from './workbench/command-palette.js';
@@ -242,6 +232,7 @@ export {
242
232
  type WorkbenchChatCommandSurfaceOptions,
243
233
  } from './chat/command-surface.js';
244
234
  export { useWorkbenchCommandDescriptors } from './commands/use-command-descriptors.js';
235
+ export { useExtensionRegistryCommandDescriptors } from './commands/use-extension-registry-command-descriptors.js';
245
236
  export {
246
237
  executeWorkbenchUserCommandAction,
247
238
  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 () => {
@@ -1,6 +1,5 @@
1
1
  import { useCallback, useEffect, useMemo, useReducer, useState, type ReactNode } from 'react';
2
2
  import { Modal } from '@workbench-kit/react/modal';
3
- import { TilepaperAppIcon } from '@workbench-kit/react';
4
3
  import { Badge, Button, IconButton } from '@workbench-kit/react/primitives';
5
4
  import {
6
5
  WorkbenchSettingsModal,
@@ -89,6 +88,8 @@ export type { WorkbenchLocaleOption, WorkbenchThemeOption } from './settings.js'
89
88
  export interface WorkbenchShellProps {
90
89
  accountManagement?: WorkbenchAccountManagementInput | undefined;
91
90
  additionalSettingsCategories?: readonly WorkbenchSettingsCategory[] | undefined;
91
+ /** Host-owned product mark shown by the default title bar. */
92
+ appIcon?: ReactNode;
92
93
  catalogTrustPolicy?: ExtensionCatalogTrustPolicy | undefined;
93
94
  catalogUrl?: string | undefined;
94
95
  commandHost?: false | Omit<WorkbenchCommandHostProps, 'onOpenSettings'>;
@@ -147,6 +148,7 @@ const OPEN_SETTINGS_COMMAND_ID = 'workbench-kit.builtin.settings.open';
147
148
  export function WorkbenchShell({
148
149
  accountManagement,
149
150
  additionalSettingsCategories,
151
+ appIcon,
150
152
  catalogTrustPolicy,
151
153
  catalogUrl = '/extension-catalog.json',
152
154
  commandHost,
@@ -383,6 +385,7 @@ export function WorkbenchShell({
383
385
  const resolvedTitleBar =
384
386
  titleBar === undefined ? (
385
387
  <WorkbenchShellTitleBar
388
+ appIcon={appIcon}
386
389
  helpContent={helpContent}
387
390
  isAuxiliarySidebarVisible={layout.auxiliaryBar.visible}
388
391
  isPanelVisible={layout.panel.visible}
@@ -725,6 +728,7 @@ export function WorkbenchShell({
725
728
  }
726
729
 
727
730
  function WorkbenchShellTitleBar({
731
+ appIcon,
728
732
  helpContent,
729
733
  isAuxiliarySidebarVisible,
730
734
  isPanelVisible,
@@ -739,6 +743,7 @@ function WorkbenchShellTitleBar({
739
743
  onTogglePanel,
740
744
  onTogglePrimarySidebar,
741
745
  }: {
746
+ appIcon: ReactNode | undefined;
742
747
  helpContent: ReactNode | undefined;
743
748
  isAuxiliarySidebarVisible: boolean;
744
749
  isPanelVisible: boolean;
@@ -756,9 +761,11 @@ function WorkbenchShellTitleBar({
756
761
  return (
757
762
  <>
758
763
  <div className="workbench-shell-titlebar__identity">
759
- <span aria-hidden className="workbench-shell-titlebar__app-icon">
760
- <TilepaperAppIcon compact />
761
- </span>
764
+ {appIcon ? (
765
+ <span aria-hidden className="workbench-shell-titlebar__app-icon">
766
+ {appIcon}
767
+ </span>
768
+ ) : null}
762
769
  <span className="workbench-shell-titlebar__title">{title}</span>
763
770
  {titleMeta ? <span className="workbench-shell-titlebar__meta">{titleMeta}</span> : null}
764
771
  </div>