@workbench-kit/shell-react 0.0.2-prototype.0.2.32 → 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 (52) 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 +87 -2
  23. package/src/field-remap/convert-palette.tsx +164 -18
  24. package/src/field-remap/demo.tsx +48 -3
  25. package/src/field-remap/detail-panel.tsx +6 -5
  26. package/src/field-remap/flow.tsx +341 -155
  27. package/src/field-remap/history.ts +99 -0
  28. package/src/field-remap/index.ts +9 -1
  29. package/src/field-remap/panel.tsx +388 -97
  30. package/src/field-remap/preview-controller.ts +122 -0
  31. package/src/field-remap/preview.tsx +171 -0
  32. package/src/field-remap/view.css +154 -2
  33. package/src/index.ts +16 -1
  34. package/src/management/keybinding-overrides-storage.ts +28 -0
  35. package/src/management/preference-settings-storage.ts +28 -0
  36. package/src/management/settings.tsx +2 -0
  37. package/src/management/use-command-management.ts +31 -26
  38. package/src/management/use-keybinding-management.ts +25 -12
  39. package/src/shell/focused-extension-services.ts +138 -0
  40. package/src/shell/host-shell.tsx +13 -10
  41. package/src/shell/persistence-diagnostic-context.ts +11 -0
  42. package/src/shell/provider.tsx +373 -69
  43. package/src/shell/settings.tsx +25 -16
  44. package/src/shell/shell.tsx +119 -78
  45. package/src/shell/view-host.tsx +23 -22
  46. package/src/storage/local-json-storage.ts +43 -29
  47. package/src/storage/persistence-diagnostics.ts +72 -0
  48. package/src/workbench/appearance-storage.ts +28 -0
  49. package/src/workbench/command-host.tsx +19 -22
  50. package/src/workbench/command-palette.ts +14 -13
  51. package/src/workbench/layout-storage.ts +52 -5
  52. package/src/workbench/use-persisted-appearance.ts +38 -12
@@ -18,13 +18,15 @@ import {
18
18
  } from '@workbench-kit/react/workbench/settings';
19
19
  import {
20
20
  applyThemeTokenOverrides,
21
- type ExtensionRegistry,
21
+ type ConfigurationRegistry,
22
+ type LocalizationRegistry,
22
23
  type PreferenceService,
24
+ type ThemeRegistry,
23
25
  } from '@workbench-kit/workbench-core';
24
26
  import type { PreferenceScope } from '@workbench-kit/workbench-config';
25
27
 
26
28
  import { isRecord } from '../is-record.js';
27
- import { useWorkbench } from './provider.js';
29
+ import { useWorkbench, type WorkbenchExtensionCatalogReader } from './provider.js';
28
30
  import { WORKBENCH_PREFERENCE_SCOPES } from './settings-constants.js';
29
31
 
30
32
  const APPEARANCE_SETTINGS_CATEGORY_ID = 'workbench.appearance';
@@ -60,8 +62,15 @@ export interface WorkbenchSettingsCategoryInput extends WorkbenchAppearanceSetti
60
62
  preferenceService: PreferenceService;
61
63
  }
62
64
 
65
+ export interface WorkbenchSettingsContributionAccess {
66
+ readonly configurations: ConfigurationRegistry;
67
+ readonly extensionCatalog: WorkbenchExtensionCatalogReader;
68
+ readonly localizations: LocalizationRegistry;
69
+ readonly themes: ThemeRegistry;
70
+ }
71
+
63
72
  export function createSettingsCategories(
64
- extensionRegistry: ExtensionRegistry,
73
+ contributions: WorkbenchSettingsContributionAccess,
65
74
  {
66
75
  activeScope,
67
76
  darkPreset,
@@ -78,9 +87,9 @@ export function createSettingsCategories(
78
87
  themeOptions,
79
88
  }: WorkbenchSettingsCategoryInput,
80
89
  ): WorkbenchSettingsCategory[] {
81
- const configurations = extensionRegistry.configurations.getConfigurations();
82
- const mergedThemeOptions = mergeThemeOptions(themeOptions, extensionRegistry.themes.getThemes());
83
- const localeOptions = buildLocaleOptions(extensionRegistry.localizations.getLocalizations());
90
+ const configurations = contributions.configurations.getConfigurations();
91
+ const mergedThemeOptions = mergeThemeOptions(themeOptions, contributions.themes.getThemes());
92
+ const localeOptions = buildLocaleOptions(contributions.localizations.getLocalizations());
84
93
  const appearanceCategory = createAppearanceSettingsCategory({
85
94
  darkPreset,
86
95
  lightPreset,
@@ -115,7 +124,7 @@ export function createSettingsCategories(
115
124
  }
116
125
 
117
126
  const contributedCategories = configurations.map(({ extensionId, configuration }) => {
118
- const extension = extensionRegistry.getExtension(extensionId);
127
+ const extension = contributions.extensionCatalog.getExtension(extensionId);
119
128
  const displayName = extension?.manifest.displayName ?? titleFromExtensionId(extensionId);
120
129
  const properties = Object.entries(configuration.properties ?? {});
121
130
 
@@ -263,29 +272,30 @@ function AppearanceSettingsSection({
263
272
  localeOptions: readonly WorkbenchLocaleOption[];
264
273
  themeOptions: readonly WorkbenchThemeOption[];
265
274
  }) {
266
- const { extensionRegistry } = useWorkbench();
275
+ const { themes } = useWorkbench();
276
+ const themeRevision = themes.getRevision();
267
277
  const containerRef = useRef<HTMLDivElement>(null);
268
278
  const previousThemeOverridesRef = useRef<Readonly<Record<string, string>> | undefined>(undefined);
269
279
  const usesAppearancePresets = lightPreset !== undefined && darkPreset !== undefined;
270
280
  const lightPresetOptions = useMemo<readonly WorkbenchThemePresetOption[]>(
271
281
  () => [
272
282
  ...LIGHT_THEME_PRESET_OPTIONS,
273
- ...extensionRegistry.themes
283
+ ...themes
274
284
  .getThemes()
275
285
  .filter((contributedTheme) => contributedTheme.mode === 'light')
276
286
  .map((contributedTheme) => ({ id: contributedTheme.id, label: contributedTheme.label })),
277
287
  ],
278
- [extensionRegistry.themes],
288
+ [themeRevision, themes],
279
289
  );
280
290
  const darkPresetOptions = useMemo<readonly WorkbenchThemePresetOption[]>(
281
291
  () => [
282
292
  ...DARK_THEME_PRESET_OPTIONS,
283
- ...extensionRegistry.themes
293
+ ...themes
284
294
  .getThemes()
285
295
  .filter((contributedTheme) => contributedTheme.mode === 'dark')
286
296
  .map((contributedTheme) => ({ id: contributedTheme.id, label: contributedTheme.label })),
287
297
  ],
288
- [extensionRegistry.themes],
298
+ [themeRevision, themes],
289
299
  );
290
300
  const selectedTheme = themeOptions.find((option) => option.id === theme) ?? themeOptions[0];
291
301
  const selectedThemeId = selectedTheme?.id ?? '';
@@ -333,9 +343,7 @@ function AppearanceSettingsSection({
333
343
  activeThemeId = selectedThemeId;
334
344
  }
335
345
 
336
- const contributedTheme = activeThemeId
337
- ? extensionRegistry.themes.getTheme(activeThemeId)
338
- : undefined;
346
+ const contributedTheme = activeThemeId ? themes.getTheme(activeThemeId) : undefined;
339
347
 
340
348
  // The actual workbench root (e.g. `.ide-root`) re-declares `data-theme-preset` locally,
341
349
  // which shadows inheritance from `documentElement` for everything rendered inside it.
@@ -357,7 +365,8 @@ function AppearanceSettingsSection({
357
365
  }
358
366
  previousThemeOverridesRef.current = contributedTheme?.tokenOverrides;
359
367
  }, [
360
- extensionRegistry.themes,
368
+ themeRevision,
369
+ themes,
361
370
  selectedColorSchemeId,
362
371
  selectedDarkPreset.id,
363
372
  selectedLightPreset.id,
@@ -10,10 +10,7 @@ import {
10
10
  type StatusBarItemModel,
11
11
  type StatusBarSectionModel,
12
12
  } from '@workbench-kit/react/workbench/shell';
13
- import {
14
- WORKBENCH_SETTINGS_CAPABILITY_ID,
15
- filterActivitiesByWhenClause,
16
- } from '@workbench-kit/workbench-core';
13
+ import { filterActivitiesByWhenClause } from '@workbench-kit/workbench-core';
17
14
  import type {
18
15
  ExtensionCatalogTrustPolicy,
19
16
  WorkbenchSettingsCapability,
@@ -24,9 +21,13 @@ import {
24
21
  resolveActiveThemePreset,
25
22
  DEFAULT_SHELL_PRESET,
26
23
  useResolvedWorkbenchTheme,
24
+ type WorkbenchCommandDescriptor,
25
+ type WorkbenchCommandRunContext,
27
26
  } from '@workbench-kit/react/workbench';
28
27
 
29
28
  import { BUILTIN_COMMANDS_VIEW_CONTAINER_ID } from '../commands/view-data.js';
29
+ import { useExtensionEnablementController } from '../extensions/extension-enablement-context.js';
30
+ import { createThemeSelectionProtectionSnapshot } from '../extensions/theme-selection-protection.js';
30
31
  import { BUILTIN_EXTENSIONS_VIEW_CONTAINER_ID } from '../extensions/view-data.js';
31
32
  import {
32
33
  filterActivityBarItems,
@@ -85,6 +86,20 @@ import {
85
86
  } from './layout-metrics.js';
86
87
  export type { WorkbenchLocaleOption, WorkbenchThemeOption } from './settings.js';
87
88
 
89
+ export interface WorkbenchShellCommandRunContext extends WorkbenchCommandRunContext {
90
+ openSettings(categoryId?: string): void;
91
+ }
92
+
93
+ export interface WorkbenchShellCommandHostProps extends Omit<
94
+ WorkbenchCommandHostProps,
95
+ 'onOpenSettings' | 'onRunCommand'
96
+ > {
97
+ onRunCommand?: (
98
+ command: WorkbenchCommandDescriptor,
99
+ context: WorkbenchShellCommandRunContext,
100
+ ) => boolean | void;
101
+ }
102
+
88
103
  export interface WorkbenchShellProps {
89
104
  accountManagement?: WorkbenchAccountManagementInput | undefined;
90
105
  additionalSettingsCategories?: readonly WorkbenchSettingsCategory[] | undefined;
@@ -92,7 +107,7 @@ export interface WorkbenchShellProps {
92
107
  appIcon?: ReactNode;
93
108
  catalogTrustPolicy?: ExtensionCatalogTrustPolicy | undefined;
94
109
  catalogUrl?: string | undefined;
95
- commandHost?: false | Omit<WorkbenchCommandHostProps, 'onOpenSettings'>;
110
+ commandHost?: false | WorkbenchShellCommandHostProps;
96
111
  compactStatus?: boolean;
97
112
  darkPreset?: string | undefined;
98
113
  editorArea?: ReactNode;
@@ -190,19 +205,42 @@ export function WorkbenchShell({
190
205
  ? resolveActiveThemePreset(resolvedWorkbenchTheme, { darkPreset, lightPreset })
191
206
  : undefined;
192
207
  const {
208
+ activities,
209
+ commands,
210
+ configurations,
193
211
  contextKeyService,
194
212
  executeCommand,
195
- extensionRegistry,
213
+ extensionActivation,
214
+ extensionCatalog,
196
215
  layoutService,
216
+ localizations,
197
217
  missingExtensionIds,
198
218
  preferenceService,
219
+ settingsCapabilityPublisher,
220
+ statusBar,
221
+ themes,
222
+ viewHostFactories,
223
+ views,
199
224
  } = useWorkbench();
225
+ const extensionEnablement = useExtensionEnablementController();
200
226
  const contextKeyRevision = useContextKeyRevision(contextKeyService);
201
227
  const contextKeySnapshot = useMemo(
202
228
  () => contextKeyService.createSnapshot(),
203
229
  [contextKeyRevision, contextKeyService],
204
230
  );
205
231
  const forceRender = useForceRender();
232
+ const themeRevision = themes.getRevision();
233
+ const themeSelectionProtection = useMemo(
234
+ () =>
235
+ createThemeSelectionProtectionSnapshot({
236
+ darkPreset,
237
+ lightPreset,
238
+ theme,
239
+ themeOptions,
240
+ themes,
241
+ }),
242
+ [darkPreset, lightPreset, theme, themeOptions, themeRevision, themes],
243
+ );
206
244
  const [preferenceRevision, bumpPreferenceRevision] = useReducer((count: number) => count + 1, 0);
207
245
  const [isHelpOpen, setHelpOpen] = useState(false);
208
246
  const [isProfileOpen, setProfileOpen] = useState(false);
@@ -228,32 +266,28 @@ export function WorkbenchShell({
228
266
  statusSections ??
229
267
  mergeWorkbenchStatusSections(
230
268
  createDefaultWorkbenchStatusSections({
231
- dependencyDiagnostics: extensionRegistry.getDependencyDiagnostics(),
232
- extensionCount: extensionRegistry.getExtensions().length,
269
+ dependencyDiagnostics: extensionCatalog.getDependencyDiagnostics(),
270
+ extensionCount: extensionCatalog.getExtensions().length,
233
271
  missingExtensionIds,
234
272
  profile,
235
273
  }),
236
- createContributedWorkbenchStatusSections(extensionRegistry.statusBar.getStatusBarItems()),
274
+ createContributedWorkbenchStatusSections(statusBar.getStatusBarItems()),
237
275
  ),
238
- [extensionRegistry, missingExtensionIds, profile, statusSections],
276
+ [extensionCatalog, missingExtensionIds, profile, statusBar, statusSections],
239
277
  );
240
278
  const activeViewContainerId = layout.sideBar.activeViewContainer;
241
279
  const activePanelViewContainerId = layout.panel.activeViewContainer;
242
- const panelViewContainers = extensionRegistry.views.getViewContainers('panel');
280
+ const panelViewContainers = views.getViewContainers('panel');
243
281
  const visibleActivities = useMemo(
244
- () =>
245
- filterActivitiesByWhenClause(
246
- extensionRegistry.activities.getActivities(),
247
- contextKeySnapshot,
248
- ),
249
- [contextKeySnapshot, extensionRegistry],
282
+ () => filterActivitiesByWhenClause(activities.getActivities(), contextKeySnapshot),
283
+ [activities, contextKeySnapshot],
250
284
  );
251
285
  const activityItems = sortActivityBarItems(
252
286
  createWorkbenchShellActivityItems({
253
287
  activeViewContainerId,
254
288
  activities: visibleActivities,
255
- viewContainers: extensionRegistry.views.getViewContainers('activitybar'),
256
- views: extensionRegistry.views.getViews(),
289
+ viewContainers: views.getViewContainers('activitybar'),
290
+ views: views.getViews(),
257
291
  }),
258
292
  layout.activityBar.itemOrder,
259
293
  );
@@ -278,6 +312,13 @@ export function WorkbenchShell({
278
312
  settingsLabel: chromeLabels.settingsLabel,
279
313
  });
280
314
 
315
+ useEffect(() => {
316
+ extensionEnablement.setThemeSelectionProtection(themeSelectionProtection);
317
+ return () => {
318
+ extensionEnablement.setThemeSelectionProtection(undefined);
319
+ };
320
+ }, [extensionEnablement, themeSelectionProtection]);
321
+
281
322
  useEffect(() => {
282
323
  if (visibleActivityItems.length === 0) {
283
324
  return;
@@ -332,30 +373,35 @@ export function WorkbenchShell({
332
373
  return [
333
374
  ...managementCategories,
334
375
  ...(additionalSettingsCategories ?? []),
335
- ...createSettingsCategories(extensionRegistry, {
336
- activeScope: settingsScopeId,
337
- darkPreset,
338
- lightPreset,
339
- locale,
340
- onDarkPresetChange,
341
- onLightPresetChange,
342
- onLocaleChange,
343
- onShellPresetChange,
344
- onThemeChange,
345
- preferenceService,
346
- shellPreset,
347
- theme,
348
- themeOptions,
349
- }),
376
+ ...createSettingsCategories(
377
+ { configurations, extensionCatalog, localizations, themes },
378
+ {
379
+ activeScope: settingsScopeId,
380
+ darkPreset,
381
+ lightPreset,
382
+ locale,
383
+ onDarkPresetChange,
384
+ onLightPresetChange,
385
+ onLocaleChange,
386
+ onShellPresetChange,
387
+ onThemeChange,
388
+ preferenceService,
389
+ shellPreset,
390
+ theme,
391
+ themeOptions,
392
+ },
393
+ ),
350
394
  ];
351
395
  }, [
352
396
  accountManagement,
353
397
  additionalSettingsCategories,
354
398
  commandHost,
355
399
  darkPreset,
356
- extensionRegistry,
400
+ configurations,
401
+ extensionCatalog,
357
402
  lightPreset,
358
403
  locale,
404
+ localizations,
359
405
  onDarkPresetChange,
360
406
  onLightPresetChange,
361
407
  onLocaleChange,
@@ -366,12 +412,14 @@ export function WorkbenchShell({
366
412
  settingsScopeId,
367
413
  shellPreset,
368
414
  theme,
415
+ themeRevision,
416
+ themes,
369
417
  themeOptions,
370
418
  ]);
371
419
  const defaultSettingsCategoryId =
372
420
  settingsCategories.find((category) => category.id === SETTINGS_EXTENSION_ID)?.id ??
373
421
  settingsCategories[0]?.id;
374
- const settingsContributionCount = extensionRegistry.configurations.getConfigurations().length;
422
+ const settingsContributionCount = configurations.getConfigurations().length;
375
423
  const showHelpModal = useCallback(() => {
376
424
  setSettingsOpen(false);
377
425
  setProfileOpen(false);
@@ -412,15 +460,17 @@ export function WorkbenchShell({
412
460
 
413
461
  useEffect(() => {
414
462
  const layoutDisposable = layoutService.onDidChangeLayout(forceRender);
415
- const viewProviderDisposable = extensionRegistry.views.onDidRegisterViewProvider(forceRender);
463
+ const viewProviderDisposable = views.onDidRegisterViewProvider(forceRender);
416
464
  const preferenceDisposable = preferenceService.onDidChangePreference(bumpPreferenceRevision);
465
+ const themeDisposable = themes.onDidChangeThemes(forceRender);
417
466
 
418
467
  return () => {
419
468
  layoutDisposable.dispose();
420
469
  viewProviderDisposable.dispose();
421
470
  preferenceDisposable.dispose();
471
+ themeDisposable.dispose();
422
472
  };
423
- }, [extensionRegistry, forceRender, layoutService, preferenceService]);
473
+ }, [forceRender, layoutService, preferenceService, themes, views]);
424
474
 
425
475
  useEffect(() => {
426
476
  if (!activeViewContainerId) {
@@ -428,13 +478,13 @@ export function WorkbenchShell({
428
478
  }
429
479
 
430
480
  for (const view of getVisibleWorkbenchViews(
431
- extensionRegistry,
481
+ { views },
432
482
  activeViewContainerId,
433
483
  contextKeySnapshot,
434
484
  )) {
435
- void extensionRegistry.activateView(view.id).then(forceRender);
485
+ void extensionActivation.activateView(view.id).then(forceRender);
436
486
  }
437
- }, [activeViewContainerId, contextKeySnapshot, extensionRegistry, forceRender]);
487
+ }, [activeViewContainerId, contextKeySnapshot, extensionActivation, forceRender, views]);
438
488
 
439
489
  useEffect(() => {
440
490
  if (!activePanelViewContainerId) {
@@ -442,54 +492,44 @@ export function WorkbenchShell({
442
492
  }
443
493
 
444
494
  for (const view of getVisibleWorkbenchViews(
445
- extensionRegistry,
495
+ { views },
446
496
  activePanelViewContainerId,
447
497
  contextKeySnapshot,
448
498
  )) {
449
- void extensionRegistry.activateView(view.id).then(forceRender);
499
+ void extensionActivation.activateView(view.id).then(forceRender);
450
500
  }
451
- }, [activePanelViewContainerId, contextKeySnapshot, extensionRegistry, forceRender]);
501
+ }, [activePanelViewContainerId, contextKeySnapshot, extensionActivation, forceRender, views]);
452
502
 
453
503
  useEffect(() => {
454
504
  if (!layout.auxiliaryBar.visible) {
455
505
  return;
456
506
  }
457
507
 
458
- for (const container of extensionRegistry.views.getViewContainers('auxiliarybar')) {
459
- for (const view of getVisibleWorkbenchViews(
460
- extensionRegistry,
461
- container.id,
462
- contextKeySnapshot,
463
- )) {
464
- void extensionRegistry.activateView(view.id).then(forceRender);
508
+ for (const container of views.getViewContainers('auxiliarybar')) {
509
+ for (const view of getVisibleWorkbenchViews({ views }, container.id, contextKeySnapshot)) {
510
+ void extensionActivation.activateView(view.id).then(forceRender);
465
511
  }
466
512
  }
467
- }, [contextKeySnapshot, extensionRegistry, forceRender, layout.auxiliaryBar.visible]);
513
+ }, [contextKeySnapshot, extensionActivation, forceRender, layout.auxiliaryBar.visible, views]);
468
514
 
469
515
  useEffect(() => {
470
- if (extensionRegistry.capabilityRegistry.has(WORKBENCH_SETTINGS_CAPABILITY_ID)) {
471
- return undefined;
472
- }
473
-
474
- const disposable = extensionRegistry.capabilityRegistry.register({
475
- id: WORKBENCH_SETTINGS_CAPABILITY_ID,
476
- get: () => settingsCapability,
477
- });
516
+ const publication = settingsCapabilityPublisher.publishSettingsCapability(settingsCapability);
517
+ if (publication.kind === 'already-registered') return undefined;
478
518
 
479
519
  return () => {
480
- disposable.dispose();
520
+ publication.disposable.dispose();
481
521
  };
482
- }, [extensionRegistry, settingsCapability]);
522
+ }, [settingsCapability, settingsCapabilityPublisher]);
483
523
 
484
- const openSettings = (categoryId?: string) => {
485
- showSettingsModal(categoryId);
486
- if (
487
- categoryId === undefined &&
488
- extensionRegistry.commands.hasCommand(OPEN_SETTINGS_COMMAND_ID)
489
- ) {
490
- void executeCommand(OPEN_SETTINGS_COMMAND_ID).catch(() => undefined);
491
- }
492
- };
524
+ const openSettings = useCallback(
525
+ (categoryId?: string) => {
526
+ showSettingsModal(categoryId);
527
+ if (categoryId === undefined && commands.hasCommand(OPEN_SETTINGS_COMMAND_ID)) {
528
+ void executeCommand(OPEN_SETTINGS_COMMAND_ID).catch(() => undefined);
529
+ }
530
+ },
531
+ [commands, executeCommand, showSettingsModal],
532
+ );
493
533
 
494
534
  const resolvedCommandHost = useMemo(():
495
535
  false | Omit<WorkbenchCommandHostProps, 'onOpenSettings'> => {
@@ -540,10 +580,10 @@ export function WorkbenchShell({
540
580
  return true;
541
581
  }
542
582
 
543
- return hostProps.onRunCommand?.(command, context) ?? false;
583
+ return hostProps.onRunCommand?.(command, { ...context, openSettings }) ?? false;
544
584
  },
545
585
  };
546
- }, [chromeLabels, commandHost, layoutService]);
586
+ }, [chromeLabels, commandHost, layoutService, openSettings]);
547
587
 
548
588
  const handleStatusItemActivate = useCallback(
549
589
  (item: StatusBarItemModel) => {
@@ -561,7 +601,7 @@ export function WorkbenchShell({
561
601
  return;
562
602
  }
563
603
 
564
- const contributed = extensionRegistry.statusBar.getStatusBarItem(item.id);
604
+ const contributed = statusBar.getStatusBarItem(item.id);
565
605
  if (contributed?.command) {
566
606
  void executeCommand(contributed.command).catch(() => undefined);
567
607
  return;
@@ -572,10 +612,11 @@ export function WorkbenchShell({
572
612
  [
573
613
  accountManagement,
574
614
  executeCommand,
575
- extensionRegistry,
576
615
  onStatusItemActivate,
616
+ openSettings,
577
617
  profile,
578
618
  showProfileModal,
619
+ statusBar,
579
620
  ],
580
621
  );
581
622
 
@@ -619,7 +660,7 @@ export function WorkbenchShell({
619
660
  node:
620
661
  primarySidebar ??
621
662
  renderDefaultPrimarySidebar(
622
- extensionRegistry,
663
+ { viewHostFactories, views },
623
664
  activeViewContainerId,
624
665
  catalogUrl,
625
666
  catalogTrustPolicy,
@@ -633,7 +674,7 @@ export function WorkbenchShell({
633
674
  auxiliarySidebar={{
634
675
  isVisible: layout.auxiliaryBar.visible,
635
676
  node: renderDefaultAuxiliarySidebar(
636
- extensionRegistry,
677
+ { viewHostFactories, views },
637
678
  contextKeySnapshot,
638
679
  catalogUrl,
639
680
  catalogTrustPolicy,
@@ -641,7 +682,7 @@ export function WorkbenchShell({
641
682
  }}
642
683
  bottomPanel={{
643
684
  isVisible: layout.panel.visible,
644
- node: renderDefaultBottomPanel(extensionRegistry, activePanelViewContainerId, {
685
+ node: renderDefaultBottomPanel({ viewHostFactories, views }, activePanelViewContainerId, {
645
686
  catalogTrustPolicy,
646
687
  catalogUrl,
647
688
  contextKeys: contextKeySnapshot,
@@ -1,9 +1,9 @@
1
1
  import { isValidElement, useEffect, useMemo, useRef, type FocusEvent, type ReactNode } from 'react';
2
2
  import type {
3
3
  ExtensionCatalogTrustPolicy,
4
- ExtensionRegistry,
5
4
  ViewHost,
6
5
  ViewHostFactoryRegistry,
6
+ ViewRegistry,
7
7
  ViewProvider,
8
8
  WorkbenchViewContribution,
9
9
  } from '@workbench-kit/workbench-core';
@@ -12,6 +12,11 @@ import {
12
12
  type WorkbenchContextKeySnapshot,
13
13
  } from '@workbench-kit/platform';
14
14
 
15
+ export interface WorkbenchViewContributionAccess {
16
+ readonly viewHostFactories: ViewHostFactoryRegistry;
17
+ readonly views: ViewRegistry;
18
+ }
19
+
15
20
  import { BuiltinChatView } from '../chat/view.js';
16
21
  import { isBuiltinChatViewRenderData } from '../chat/view-data.js';
17
22
  import { BuiltinCommandsView } from '../commands/view.js';
@@ -24,15 +29,15 @@ import { BuiltinSearchView } from '../explorer/search-view.js';
24
29
  import { isBuiltinSearchViewRenderData } from '../explorer/search-view-data.js';
25
30
 
26
31
  export function renderDefaultPrimarySidebar(
27
- extensionRegistry: Pick<ExtensionRegistry, 'viewHostFactories' | 'views'>,
32
+ access: WorkbenchViewContributionAccess,
28
33
  activeViewContainerId: string | undefined,
29
34
  catalogUrl?: string | undefined,
30
35
  catalogTrustPolicy?: ExtensionCatalogTrustPolicy | undefined,
31
36
  contextKeys: WorkbenchContextKeySnapshot = {},
32
37
  ) {
33
38
  const views = activeViewContainerId
34
- ? getVisibleWorkbenchViews(extensionRegistry, activeViewContainerId, contextKeys)
35
- : filterWorkbenchContributionsByWhenClause(extensionRegistry.views.getViews(), contextKeys);
39
+ ? getVisibleWorkbenchViews(access, activeViewContainerId, contextKeys)
40
+ : filterWorkbenchContributionsByWhenClause(access.views.getViews(), contextKeys);
36
41
  if (views.length === 0) {
37
42
  return <aside aria-label="Primary sidebar" />;
38
43
  }
@@ -42,33 +47,33 @@ export function renderDefaultPrimarySidebar(
42
47
  aria-label="Primary sidebar"
43
48
  className="workbench-primary-side-bar shell-react-sidebar-host"
44
49
  >
45
- {renderWorkbenchViews(extensionRegistry, views, { catalogTrustPolicy, catalogUrl })}
50
+ {renderWorkbenchViews(access, views, { catalogTrustPolicy, catalogUrl })}
46
51
  </aside>
47
52
  );
48
53
  }
49
54
 
50
55
  export function renderDefaultAuxiliarySidebar(
51
- extensionRegistry: Pick<ExtensionRegistry, 'viewHostFactories' | 'views'>,
56
+ access: WorkbenchViewContributionAccess,
52
57
  contextKeys: WorkbenchContextKeySnapshot,
53
58
  catalogUrl?: string | undefined,
54
59
  catalogTrustPolicy?: ExtensionCatalogTrustPolicy | undefined,
55
60
  ) {
56
- const views = extensionRegistry.views
61
+ const views = access.views
57
62
  .getViewContainers('auxiliarybar')
58
- .flatMap((container) => getVisibleWorkbenchViews(extensionRegistry, container.id, contextKeys));
63
+ .flatMap((container) => getVisibleWorkbenchViews(access, container.id, contextKeys));
59
64
 
60
65
  return (
61
66
  <aside
62
67
  aria-label="Secondary Side Bar"
63
68
  className="workbench-auxiliary-side-bar shell-react-sidebar-host"
64
69
  >
65
- {renderWorkbenchViews(extensionRegistry, views, { catalogTrustPolicy, catalogUrl })}
70
+ {renderWorkbenchViews(access, views, { catalogTrustPolicy, catalogUrl })}
66
71
  </aside>
67
72
  );
68
73
  }
69
74
 
70
75
  export function renderDefaultBottomPanel(
71
- extensionRegistry: Pick<ExtensionRegistry, 'viewHostFactories' | 'views'>,
76
+ access: WorkbenchViewContributionAccess,
72
77
  activeViewContainerId: string | undefined,
73
78
  options: {
74
79
  catalogTrustPolicy?: ExtensionCatalogTrustPolicy | undefined;
@@ -77,7 +82,7 @@ export function renderDefaultBottomPanel(
77
82
  onActiveViewContainerChange?: ((viewContainerId: string) => void) | undefined;
78
83
  } = {},
79
84
  ) {
80
- const containers = extensionRegistry.views.getViewContainers('panel');
85
+ const containers = access.views.getViewContainers('panel');
81
86
  if (containers.length === 0) {
82
87
  return (
83
88
  <section aria-label="Panel" className="workbench-bottom-panel">
@@ -92,11 +97,7 @@ export function renderDefaultBottomPanel(
92
97
  ? activeViewContainerId
93
98
  : containers[0]?.id;
94
99
  const views = resolvedActiveViewContainerId
95
- ? getVisibleWorkbenchViews(
96
- extensionRegistry,
97
- resolvedActiveViewContainerId,
98
- options.contextKeys ?? {},
99
- )
100
+ ? getVisibleWorkbenchViews(access, resolvedActiveViewContainerId, options.contextKeys ?? {})
100
101
  : [];
101
102
 
102
103
  return (
@@ -127,7 +128,7 @@ export function renderDefaultBottomPanel(
127
128
  {views.length === 0 ? (
128
129
  <div className="workbench-bottom-panel__empty">No views in this panel container.</div>
129
130
  ) : (
130
- renderWorkbenchViews(extensionRegistry, views, {
131
+ renderWorkbenchViews(access, views, {
131
132
  catalogTrustPolicy: options.catalogTrustPolicy,
132
133
  catalogUrl: options.catalogUrl,
133
134
  sectionClassName: 'workbench-bottom-panel__view',
@@ -139,18 +140,18 @@ export function renderDefaultBottomPanel(
139
140
  }
140
141
 
141
142
  export function getVisibleWorkbenchViews(
142
- extensionRegistry: Pick<ExtensionRegistry, 'views'>,
143
+ access: Pick<WorkbenchViewContributionAccess, 'views'>,
143
144
  viewContainerId: string,
144
145
  contextKeys: WorkbenchContextKeySnapshot,
145
146
  ): WorkbenchViewContribution[] {
146
147
  return filterWorkbenchContributionsByWhenClause(
147
- extensionRegistry.views.getViews(viewContainerId),
148
+ access.views.getViews(viewContainerId),
148
149
  contextKeys,
149
150
  );
150
151
  }
151
152
 
152
153
  function renderWorkbenchViews(
153
- extensionRegistry: Pick<ExtensionRegistry, 'viewHostFactories' | 'views'>,
154
+ access: WorkbenchViewContributionAccess,
154
155
  views: readonly WorkbenchViewContribution[],
155
156
  options: {
156
157
  catalogTrustPolicy?: ExtensionCatalogTrustPolicy | undefined;
@@ -169,8 +170,8 @@ function renderWorkbenchViews(
169
170
  catalogTrustPolicy={options.catalogTrustPolicy}
170
171
  catalogUrl={options.catalogUrl}
171
172
  fallback={view.name}
172
- provider={extensionRegistry.views.getViewProvider(view.id)}
173
- viewHostFactories={extensionRegistry.viewHostFactories}
173
+ provider={access.views.getViewProvider(view.id)}
174
+ viewHostFactories={access.viewHostFactories}
174
175
  viewId={view.id}
175
176
  />
176
177
  </section>