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

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
@@ -0,0 +1,122 @@
1
+ import {
2
+ convertMappedInputs,
3
+ isAbortError,
4
+ type ConvertMappedInputsInput,
5
+ type ConvertToShapeResult,
6
+ } from '@workbench-kit/field-remap';
7
+
8
+ import type { FieldRemapPreviewState } from './preview.js';
9
+
10
+ export type FieldRemapPreviewCommand =
11
+ | { readonly kind: 'hidden' }
12
+ | { readonly kind: 'no-sample' }
13
+ | {
14
+ readonly kind: 'evaluate';
15
+ /** Stable signature of every durable input, including transform implementations. */
16
+ readonly revision: string;
17
+ readonly input: Omit<ConvertMappedInputsInput, 'signal'>;
18
+ };
19
+
20
+ type FieldRemapPreviewEvaluator = (
21
+ input: ConvertMappedInputsInput,
22
+ ) => Promise<ConvertToShapeResult>;
23
+
24
+ export interface FieldRemapPreviewController {
25
+ readonly getSnapshot: () => FieldRemapPreviewState;
26
+ readonly subscribe: (listener: () => void) => () => void;
27
+ readonly update: (command: FieldRemapPreviewCommand) => void;
28
+ readonly dispose: () => void;
29
+ }
30
+
31
+ /** Package-private execution owner. Public Flow surfaces consume only its snapshot. */
32
+ export function createFieldRemapPreviewController(
33
+ evaluate: FieldRemapPreviewEvaluator = convertMappedInputs,
34
+ ): FieldRemapPreviewController {
35
+ let snapshot: FieldRemapPreviewState = {
36
+ status: 'unavailable',
37
+ reason: 'no-sample',
38
+ };
39
+ let generation = 0;
40
+ let disposed = false;
41
+ let abortController: AbortController | undefined;
42
+ let activeRevision: string | undefined;
43
+ const listeners = new Set<() => void>();
44
+
45
+ const publish = (next: FieldRemapPreviewState): void => {
46
+ if (disposed) {
47
+ return;
48
+ }
49
+ snapshot = next;
50
+ for (const listener of listeners) {
51
+ listener();
52
+ }
53
+ };
54
+
55
+ return {
56
+ getSnapshot: () => snapshot,
57
+ subscribe: (listener) => {
58
+ if (disposed) {
59
+ return () => {};
60
+ }
61
+ listeners.add(listener);
62
+ return () => listeners.delete(listener);
63
+ },
64
+ update: (command) => {
65
+ if (disposed) {
66
+ return;
67
+ }
68
+ if (command.kind === 'evaluate' && command.revision === activeRevision) {
69
+ return;
70
+ }
71
+
72
+ abortController?.abort();
73
+ generation += 1;
74
+ const currentGeneration = generation;
75
+ abortController = undefined;
76
+
77
+ if (command.kind !== 'evaluate') {
78
+ activeRevision = undefined;
79
+ publish({ status: 'unavailable', reason: command.kind });
80
+ return;
81
+ }
82
+
83
+ activeRevision = command.revision;
84
+ const controller = new AbortController();
85
+ abortController = controller;
86
+ publish({ status: 'loading' });
87
+
88
+ void evaluate({ ...command.input, signal: controller.signal })
89
+ .then((result) => {
90
+ if (disposed || controller.signal.aborted || currentGeneration !== generation) {
91
+ return;
92
+ }
93
+ publish({ status: 'ready', result });
94
+ })
95
+ .catch((error: unknown) => {
96
+ if (
97
+ disposed ||
98
+ controller.signal.aborted ||
99
+ currentGeneration !== generation ||
100
+ isAbortError(error)
101
+ ) {
102
+ return;
103
+ }
104
+ publish({
105
+ status: 'error',
106
+ message: error instanceof Error ? error.message : String(error),
107
+ });
108
+ });
109
+ },
110
+ dispose: () => {
111
+ if (disposed) {
112
+ return;
113
+ }
114
+ disposed = true;
115
+ generation += 1;
116
+ abortController?.abort();
117
+ abortController = undefined;
118
+ activeRevision = undefined;
119
+ listeners.clear();
120
+ },
121
+ };
122
+ }
@@ -0,0 +1,171 @@
1
+ import { useMemo, type JSX } from 'react';
2
+ import type { ConvertToShapeResult, MappingEdge } from '@workbench-kit/field-remap';
3
+
4
+ import { defaultFieldRemapChromeLabels, type FieldRemapChromeLabels } from './chrome-labels.js';
5
+ import type { FieldRemapSelection } from './flow-ops.js';
6
+
7
+ export type FieldRemapPreviewState =
8
+ | {
9
+ readonly status: 'unavailable';
10
+ readonly reason: 'hidden' | 'no-sample';
11
+ }
12
+ | { readonly status: 'loading' }
13
+ | {
14
+ readonly status: 'ready';
15
+ readonly result: ConvertToShapeResult;
16
+ }
17
+ | {
18
+ readonly status: 'error';
19
+ readonly message: string;
20
+ };
21
+
22
+ export type FieldRemapPreviewProjection =
23
+ | FieldRemapPreviewState
24
+ | {
25
+ readonly status: 'unsupported';
26
+ readonly reason: 'draft' | 'stale-selection';
27
+ }
28
+ | {
29
+ readonly status: 'value';
30
+ readonly scope: 'binding' | 'document';
31
+ readonly value: unknown;
32
+ readonly notice?: 'operator-intermediate' | 'transform-step-intermediate';
33
+ };
34
+
35
+ export function resolveFieldRemapPreviewProjection(
36
+ preview: FieldRemapPreviewState,
37
+ selection: FieldRemapSelection,
38
+ edges: readonly MappingEdge[],
39
+ operatorExists: boolean,
40
+ ): FieldRemapPreviewProjection {
41
+ if (preview.status !== 'ready') {
42
+ return preview;
43
+ }
44
+ if (!selection) {
45
+ return { status: 'value', scope: 'document', value: preview.result.output };
46
+ }
47
+ if (selection.kind === 'draft') {
48
+ return { status: 'unsupported', reason: 'draft' };
49
+ }
50
+ if (selection.kind === 'operator') {
51
+ return operatorExists
52
+ ? {
53
+ status: 'value',
54
+ scope: 'document',
55
+ value: preview.result.output,
56
+ notice: 'operator-intermediate',
57
+ }
58
+ : { status: 'unsupported', reason: 'stale-selection' };
59
+ }
60
+
61
+ const edge = edges.find((item) => item.id === selection.edgeId);
62
+ if (!edge) {
63
+ return { status: 'unsupported', reason: 'stale-selection' };
64
+ }
65
+ if (
66
+ selection.kind === 'transformStep' &&
67
+ (selection.stepIndex < 0 || selection.stepIndex >= (edge.transformIds?.length ?? 0))
68
+ ) {
69
+ return { status: 'unsupported', reason: 'stale-selection' };
70
+ }
71
+
72
+ const slot = preview.result.slots.find((item) => item.edgeId === edge.id);
73
+ if (!slot) {
74
+ return { status: 'unsupported', reason: 'stale-selection' };
75
+ }
76
+ return {
77
+ status: 'value',
78
+ scope: 'binding',
79
+ value: slot.value,
80
+ ...(selection.kind === 'transformStep'
81
+ ? { notice: 'transform-step-intermediate' as const }
82
+ : {}),
83
+ };
84
+ }
85
+
86
+ function previewLabel(labels: FieldRemapChromeLabels, key: keyof FieldRemapChromeLabels): string {
87
+ return labels[key] ?? defaultFieldRemapChromeLabels[key];
88
+ }
89
+
90
+ function formatPreviewValue(value: unknown): string {
91
+ try {
92
+ return JSON.stringify(value, null, 2) ?? String(value);
93
+ } catch {
94
+ return String(value);
95
+ }
96
+ }
97
+
98
+ export interface FieldRemapPreviewRailProps {
99
+ readonly preview: FieldRemapPreviewState;
100
+ readonly selection: FieldRemapSelection;
101
+ readonly edges: readonly MappingEdge[];
102
+ readonly operatorExists: boolean;
103
+ readonly labels: FieldRemapChromeLabels;
104
+ }
105
+
106
+ export function FieldRemapPreviewRail({
107
+ preview,
108
+ selection,
109
+ edges,
110
+ operatorExists,
111
+ labels,
112
+ }: FieldRemapPreviewRailProps): JSX.Element {
113
+ const projection = useMemo(
114
+ () => resolveFieldRemapPreviewProjection(preview, selection, edges, operatorExists),
115
+ [edges, operatorExists, preview, selection],
116
+ );
117
+
118
+ return (
119
+ <aside
120
+ className="workbench-field-remap-preview"
121
+ data-testid="field-remap-preview"
122
+ aria-label={previewLabel(labels, 'previewTitle')}
123
+ >
124
+ <h4>{previewLabel(labels, 'previewTitle')}</h4>
125
+ {projection.status === 'loading' ? (
126
+ <p role="status" data-testid="field-remap-preview-loading">
127
+ {previewLabel(labels, 'previewLoading')}
128
+ </p>
129
+ ) : null}
130
+ {projection.status === 'error' ? (
131
+ <p role="alert" data-testid="field-remap-preview-error">
132
+ {previewLabel(labels, 'previewError')}: {projection.message}
133
+ </p>
134
+ ) : null}
135
+ {projection.status === 'unsupported' ? (
136
+ <p role="status" data-testid="field-remap-preview-unavailable">
137
+ {previewLabel(
138
+ labels,
139
+ projection.reason === 'draft'
140
+ ? 'previewDraftUnavailable'
141
+ : 'previewSelectionUnavailable',
142
+ )}
143
+ </p>
144
+ ) : null}
145
+ {projection.status === 'value' ? (
146
+ <>
147
+ <p className="workbench-field-remap-preview__scope">
148
+ {previewLabel(
149
+ labels,
150
+ projection.scope === 'binding' ? 'previewBindingResult' : 'previewDocumentResult',
151
+ )}
152
+ </p>
153
+ {projection.notice ? (
154
+ <p
155
+ className="workbench-field-remap-preview__notice"
156
+ data-testid="field-remap-preview-notice"
157
+ >
158
+ {previewLabel(
159
+ labels,
160
+ projection.notice === 'transform-step-intermediate'
161
+ ? 'previewStepIntermediateUnavailable'
162
+ : 'previewOperatorIntermediateUnavailable',
163
+ )}
164
+ </p>
165
+ ) : null}
166
+ <pre data-testid="field-remap-preview-value">{formatPreviewValue(projection.value)}</pre>
167
+ </>
168
+ ) : null}
169
+ </aside>
170
+ );
171
+ }
@@ -345,6 +345,7 @@
345
345
  .workbench-field-remap-flow[data-chrome='embed'] .workbench-field-remap-convert-palette,
346
346
  .workbench-field-remap-flow[data-chrome='embed'] .workbench-field-remap-flow__canvas,
347
347
  .workbench-field-remap-flow[data-chrome='embed'] .workbench-field-remap-detail,
348
+ .workbench-field-remap-flow[data-chrome='embed'] .workbench-field-remap-preview,
348
349
  .workbench-field-remap-flow[data-chrome='embed'] .workbench-field-remap-convert-note {
349
350
  border-radius: 0;
350
351
  }
@@ -359,6 +360,7 @@
359
360
  }
360
361
 
361
362
  .workbench-field-remap-flow[data-chrome='embed'] .workbench-field-remap-detail,
363
+ .workbench-field-remap-flow[data-chrome='embed'] .workbench-field-remap-preview,
362
364
  .workbench-field-remap-flow[data-chrome='embed'] .workbench-field-remap-convert-note {
363
365
  border: 0;
364
366
  border-inline-start: 1px solid var(--vscode-panel-border, var(--color-border));
@@ -374,6 +376,9 @@
374
376
  .workbench-field-remap-flow[data-chrome='embed']
375
377
  .workbench-field-remap-flow__workspace--split
376
378
  .workbench-field-remap-detail,
379
+ .workbench-field-remap-flow[data-chrome='embed']
380
+ .workbench-field-remap-flow__workspace--split
381
+ .workbench-field-remap-preview,
377
382
  .workbench-field-remap-flow[data-chrome='embed']
378
383
  .workbench-field-remap-flow__workspace--split
379
384
  .workbench-field-remap-convert-note {
@@ -401,6 +406,7 @@
401
406
  }
402
407
 
403
408
  .workbench-field-remap-flow[data-chrome='embed'] .workbench-field-remap-detail,
409
+ .workbench-field-remap-flow[data-chrome='embed'] .workbench-field-remap-preview,
404
410
  .workbench-field-remap-flow[data-chrome='embed'] .workbench-field-remap-convert-note {
405
411
  border-inline-start: 0;
406
412
  border-block-start: 1px solid var(--vscode-panel-border, var(--color-border));
@@ -439,6 +445,7 @@
439
445
  }
440
446
 
441
447
  .workbench-field-remap-convert-palette__header,
448
+ .workbench-field-remap-convert-palette__filter,
442
449
  .workbench-field-remap-convert-palette__place,
443
450
  .workbench-field-remap-convert-palette__operators {
444
451
  flex: 0 0 auto;
@@ -457,6 +464,14 @@
457
464
  font-size: 0.7rem;
458
465
  }
459
466
 
467
+ .workbench-field-remap-convert-palette__empty {
468
+ flex: 1 1 auto;
469
+ margin: 0;
470
+ padding: 0.35rem;
471
+ font-size: 0.75rem;
472
+ color: var(--vscode-descriptionForeground, var(--color-text-muted));
473
+ }
474
+
460
475
  .workbench-field-remap-convert-palette__operators {
461
476
  display: flex;
462
477
  flex-direction: column;
@@ -773,6 +788,63 @@
773
788
  font-size: 0.8rem;
774
789
  }
775
790
 
791
+ .workbench-field-remap-flow__side-rail {
792
+ display: flex;
793
+ flex-direction: column;
794
+ min-width: 0;
795
+ min-height: 0;
796
+ height: 100%;
797
+ overflow: auto;
798
+ }
799
+
800
+ .workbench-field-remap-preview {
801
+ display: flex;
802
+ flex-direction: column;
803
+ gap: 0.6rem;
804
+ min-width: 0;
805
+ padding: 0.75rem;
806
+ border: 1px solid var(--vscode-panel-border, var(--color-border));
807
+ border-radius: 0.375rem;
808
+ background: var(--vscode-editor-background, var(--color-bg));
809
+ color: var(--vscode-foreground, var(--color-text));
810
+ font-size: 0.8rem;
811
+ }
812
+
813
+ .workbench-field-remap-flow__side-rail > .workbench-field-remap-preview:not(:first-child) {
814
+ border-block-start: 0;
815
+ border-start-start-radius: 0;
816
+ border-start-end-radius: 0;
817
+ }
818
+
819
+ .workbench-field-remap-preview h4,
820
+ .workbench-field-remap-preview p {
821
+ margin: 0;
822
+ }
823
+
824
+ .workbench-field-remap-preview h4 {
825
+ font-size: 0.85rem;
826
+ }
827
+
828
+ .workbench-field-remap-preview__scope,
829
+ .workbench-field-remap-preview__notice {
830
+ color: var(--vscode-descriptionForeground, var(--color-text-muted));
831
+ }
832
+
833
+ .workbench-field-remap-preview pre {
834
+ box-sizing: border-box;
835
+ max-height: 16rem;
836
+ margin: 0;
837
+ padding: 0.6rem;
838
+ overflow: auto;
839
+ border: 1px solid var(--vscode-panel-border, var(--color-border));
840
+ border-radius: 0.25rem;
841
+ background: var(--vscode-textCodeBlock-background, var(--color-surface));
842
+ color: inherit;
843
+ font: 0.75rem/1.45 var(--vscode-editor-font-family, ui-monospace, monospace);
844
+ white-space: pre-wrap;
845
+ word-break: break-word;
846
+ }
847
+
776
848
  .workbench-field-remap-convert-note {
777
849
  border-color: var(--vscode-focusBorder, var(--color-accent, #3794ff));
778
850
  background: var(--vscode-editor-background, var(--color-bg));
package/src/index.ts CHANGED
@@ -4,7 +4,12 @@ export {
4
4
  WorkbenchProvider,
5
5
  useWorkbench,
6
6
  type WorkbenchContextValue,
7
+ type WorkbenchExtensionActivationAccess,
8
+ type WorkbenchExtensionActivationStateReader,
9
+ type WorkbenchExtensionCatalogReader,
7
10
  type WorkbenchProviderProps,
11
+ type WorkbenchSettingsCapabilityPublication,
12
+ type WorkbenchSettingsCapabilityPublisher,
8
13
  type WorkbenchStorageAdapter,
9
14
  type WorkbenchWorkspaceHostPort,
10
15
  } from './shell/provider.js';
@@ -99,12 +104,20 @@ export {
99
104
  type SampleJdwWidgetPreviewRenderData,
100
105
  } from './jdw/document-view-data.js';
101
106
  export { SampleFieldRemapDemo, type SampleFieldRemapDemoProps } from './field-remap/demo.js';
102
- export { FieldRemapPanel, type FieldRemapPanelProps } from './field-remap/panel.js';
107
+ export {
108
+ FieldRemapPanel,
109
+ type FieldRemapHistoryActions,
110
+ type FieldRemapHistoryAvailability,
111
+ type FieldRemapHistoryOwner,
112
+ type FieldRemapHistorySnapshot,
113
+ type FieldRemapPanelProps,
114
+ } from './field-remap/panel.js';
103
115
  export {
104
116
  FieldRemapFlowMapper,
105
117
  type FieldRemapFlowActions,
106
118
  type FieldRemapFlowMapperProps,
107
119
  } from './field-remap/flow.js';
120
+ export type { FieldRemapPreviewState } from './field-remap/preview.js';
108
121
  export {
109
122
  defaultFieldRemapChromeLabels,
110
123
  fieldRemapChromeLabelKeys,
@@ -206,6 +219,8 @@ export {
206
219
  export {
207
220
  WorkbenchShell,
208
221
  type WorkbenchLocaleOption,
222
+ type WorkbenchShellCommandHostProps,
223
+ type WorkbenchShellCommandRunContext,
209
224
  type WorkbenchShellProps,
210
225
  type WorkbenchThemeOption,
211
226
  } from './shell/shell.js';
@@ -3,14 +3,19 @@ import {
3
3
  type WorkbenchKeybindingDefinition,
4
4
  } from '@workbench-kit/workbench-config';
5
5
  import {
6
+ type WorkbenchPersistenceDiagnosticOptions,
7
+ type WorkbenchPersistenceReadResult,
8
+ type WorkbenchPersistenceWriteResult,
6
9
  type WorkbenchStorageReader,
7
10
  type WorkbenchStorageWriter,
8
11
  } from '@workbench-kit/workbench-core';
9
12
 
10
13
  import {
11
14
  readLocalJsonStorage,
15
+ readLocalJsonStorageResult,
12
16
  resolveLocalWorkbenchStorage,
13
17
  writeLocalJsonStorage,
18
+ writeLocalJsonStorageResult,
14
19
  } from '../storage/local-json-storage.js';
15
20
 
16
21
  export const DEFAULT_WORKBENCH_KEYBINDING_STORAGE_KEY = 'workbench-kit/.workbench/keybindings';
@@ -26,6 +31,20 @@ export function readPersistedKeybindingOverrides(
26
31
  return readLocalJsonStorage(storageKey, parseWorkbenchKeybindingsConfig, () => [], storage);
27
32
  }
28
33
 
34
+ export function readPersistedKeybindingOverridesResult(
35
+ storageKey = DEFAULT_WORKBENCH_KEYBINDING_STORAGE_KEY,
36
+ storage?: WorkbenchStorageReader,
37
+ options: WorkbenchPersistenceDiagnosticOptions = {},
38
+ ): WorkbenchPersistenceReadResult<readonly WorkbenchKeybindingDefinition[]> {
39
+ return readLocalJsonStorageResult(
40
+ storageKey,
41
+ parseWorkbenchKeybindingsConfig,
42
+ () => [],
43
+ storage,
44
+ options,
45
+ );
46
+ }
47
+
29
48
  export function writePersistedKeybindingOverrides(
30
49
  overrides: readonly WorkbenchKeybindingDefinition[],
31
50
  storageKey = DEFAULT_WORKBENCH_KEYBINDING_STORAGE_KEY,
@@ -33,3 +52,12 @@ export function writePersistedKeybindingOverrides(
33
52
  ): void {
34
53
  writeLocalJsonStorage(storageKey, overrides, storage, { errorMode: 'throw' });
35
54
  }
55
+
56
+ export function writePersistedKeybindingOverridesResult(
57
+ overrides: readonly WorkbenchKeybindingDefinition[],
58
+ storageKey = DEFAULT_WORKBENCH_KEYBINDING_STORAGE_KEY,
59
+ storage?: WorkbenchStorageWriter,
60
+ options: WorkbenchPersistenceDiagnosticOptions = {},
61
+ ): WorkbenchPersistenceWriteResult {
62
+ return writeLocalJsonStorageResult(storageKey, overrides, storage, options);
63
+ }
@@ -3,14 +3,19 @@ import {
3
3
  type WorkbenchSettingsConfig,
4
4
  } from '@workbench-kit/workbench-config';
5
5
  import {
6
+ type WorkbenchPersistenceDiagnosticOptions,
7
+ type WorkbenchPersistenceReadResult,
8
+ type WorkbenchPersistenceWriteResult,
6
9
  type WorkbenchStorageReader,
7
10
  type WorkbenchStorageWriter,
8
11
  } from '@workbench-kit/workbench-core';
9
12
 
10
13
  import {
11
14
  readLocalJsonStorage,
15
+ readLocalJsonStorageResult,
12
16
  resolveLocalWorkbenchStorage,
13
17
  writeLocalJsonStorage,
18
+ writeLocalJsonStorageResult,
14
19
  } from '../storage/local-json-storage.js';
15
20
 
16
21
  export const DEFAULT_WORKBENCH_LOCAL_PREFERENCE_STORAGE_KEY =
@@ -27,6 +32,20 @@ export function readPersistedLocalPreferences(
27
32
  return readLocalJsonStorage(storageKey, parseWorkbenchSettingsConfig, () => ({}), storage);
28
33
  }
29
34
 
35
+ export function readPersistedLocalPreferencesResult(
36
+ storageKey = DEFAULT_WORKBENCH_LOCAL_PREFERENCE_STORAGE_KEY,
37
+ storage?: WorkbenchStorageReader,
38
+ options: WorkbenchPersistenceDiagnosticOptions = {},
39
+ ): WorkbenchPersistenceReadResult<WorkbenchSettingsConfig> {
40
+ return readLocalJsonStorageResult(
41
+ storageKey,
42
+ parseWorkbenchSettingsConfig,
43
+ () => ({}),
44
+ storage,
45
+ options,
46
+ );
47
+ }
48
+
30
49
  export function writePersistedLocalPreferences(
31
50
  values: WorkbenchSettingsConfig,
32
51
  storageKey = DEFAULT_WORKBENCH_LOCAL_PREFERENCE_STORAGE_KEY,
@@ -34,3 +53,12 @@ export function writePersistedLocalPreferences(
34
53
  ): void {
35
54
  writeLocalJsonStorage(storageKey, values, storage, { errorMode: 'throw' });
36
55
  }
56
+
57
+ export function writePersistedLocalPreferencesResult(
58
+ values: WorkbenchSettingsConfig,
59
+ storageKey = DEFAULT_WORKBENCH_LOCAL_PREFERENCE_STORAGE_KEY,
60
+ storage?: WorkbenchStorageWriter,
61
+ options: WorkbenchPersistenceDiagnosticOptions = {},
62
+ ): WorkbenchPersistenceWriteResult {
63
+ return writeLocalJsonStorageResult(storageKey, values, storage, options);
64
+ }
@@ -55,6 +55,7 @@ export function WorkbenchExtensionManagementSettings({
55
55
  installTrustRecords,
56
56
  rememberInstallTrust,
57
57
  toggleInstalledEntry,
58
+ uninstallInstalledEntry,
58
59
  } = useExtensionManagementModel({ catalogTrustPolicy, catalogUrl });
59
60
 
60
61
  const isInstallTrusted = useCallback(
@@ -77,6 +78,7 @@ export function WorkbenchExtensionManagementSettings({
77
78
  onInstall={installCatalogEntry}
78
79
  onRememberInstallTrust={rememberInstallTrust}
79
80
  onToggleEnabled={toggleInstalledEntry}
81
+ onUninstall={uninstallInstalledEntry}
80
82
  />
81
83
  );
82
84
  }