@workbench-kit/shell-react 0.0.2-prototype.0.2.41 → 0.0.2-prototype.0.2.44

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 (39) hide show
  1. package/README.md +81 -2
  2. package/package.json +14 -10
  3. package/src/editor/workspace-reconcile.tsx +12 -7
  4. package/src/extensions/extension-enablement-controller.ts +40 -9
  5. package/src/extensions/theme-selection-protection.ts +80 -55
  6. package/src/field-remap/chrome-labels.ts +117 -0
  7. package/src/field-remap/convert-note-editor.tsx +119 -104
  8. package/src/field-remap/convert-palette.tsx +6 -0
  9. package/src/field-remap/demo.tsx +8 -2
  10. package/src/field-remap/detail-panel.tsx +535 -434
  11. package/src/field-remap/document-io.tsx +299 -0
  12. package/src/field-remap/drag-payload.ts +48 -0
  13. package/src/field-remap/flow-adapter.ts +216 -88
  14. package/src/field-remap/flow-ops.ts +150 -0
  15. package/src/field-remap/flow.tsx +1227 -272
  16. package/src/field-remap/index.ts +2 -0
  17. package/src/field-remap/io-class-browse.tsx +34 -22
  18. package/src/field-remap/keyboard.ts +16 -0
  19. package/src/field-remap/modal-detail.tsx +34 -0
  20. package/src/field-remap/panel.tsx +110 -14
  21. package/src/field-remap/transform-options-editor.tsx +68 -48
  22. package/src/field-remap/view.css +107 -189
  23. package/src/index.ts +7 -0
  24. package/src/keybinding-management-settings.ts +4 -0
  25. package/src/management/keybinding-overrides-storage.ts +48 -23
  26. package/src/management/keybinding-settings-view.tsx +31 -0
  27. package/src/management/keybinding-settings.tsx +4 -12
  28. package/src/management/use-keybinding-management.ts +72 -22
  29. package/src/shell/appearance-catalog.ts +453 -0
  30. package/src/shell/appearance-controller.ts +331 -0
  31. package/src/shell/appearance-presentation.ts +269 -0
  32. package/src/shell/provider.tsx +157 -19
  33. package/src/shell/settings.tsx +282 -153
  34. package/src/shell/shell.tsx +88 -18
  35. package/src/workbench/appearance-storage.ts +2 -2
  36. package/src/workbench/command-host-controller.tsx +223 -0
  37. package/src/workbench/command-host.tsx +100 -150
  38. package/src/workbench/keybinding-bridge.ts +84 -38
  39. package/src/workbench/shell-command-registration.ts +27 -2
@@ -0,0 +1,331 @@
1
+ import { applyThemeTokenOverrides, sanitizeThemeTokenValue } from '@workbench-kit/workbench-core';
2
+
3
+ export type WorkbenchAppearanceOverrideSnapshot = Readonly<Record<string, string>>;
4
+
5
+ export interface WorkbenchDocumentAppearanceOverrideController {
6
+ getSnapshot(): WorkbenchAppearanceOverrideSnapshot | undefined;
7
+ update(
8
+ tokenOverrides: Readonly<Record<string, string>> | undefined,
9
+ ): WorkbenchAppearanceOverrideSnapshot | undefined;
10
+ dispose(): void;
11
+ }
12
+
13
+ export interface WorkbenchAppearanceDiagnosticSnapshot {
14
+ readonly unresolvedTheme?: string | undefined;
15
+ readonly unresolvedThemePreset?: string | undefined;
16
+ }
17
+
18
+ export interface WorkbenchDocumentAppearanceDiagnosticController {
19
+ update(snapshot: WorkbenchAppearanceDiagnosticSnapshot): void;
20
+ dispose(): void;
21
+ }
22
+
23
+ interface PropertyBaseline {
24
+ readonly priority: string;
25
+ readonly value: string;
26
+ }
27
+
28
+ interface OverrideOwner {
29
+ readonly generation: symbol;
30
+ snapshot: WorkbenchAppearanceOverrideSnapshot | undefined;
31
+ }
32
+
33
+ interface DocumentOverrideState {
34
+ readonly baselines: Map<string, PropertyBaseline>;
35
+ readonly owners: OverrideOwner[];
36
+ readonly target: HTMLElement;
37
+ }
38
+
39
+ const documentOverrideStates = new WeakMap<HTMLElement, DocumentOverrideState>();
40
+
41
+ const APPEARANCE_DIAGNOSTIC_ATTRIBUTES = Object.freeze({
42
+ unresolvedTheme: 'data-workbench-unresolved-theme',
43
+ unresolvedThemePreset: 'data-workbench-unresolved-theme-preset',
44
+ } as const);
45
+
46
+ interface DiagnosticOwner {
47
+ readonly generation: symbol;
48
+ snapshot: WorkbenchAppearanceDiagnosticSnapshot;
49
+ }
50
+
51
+ interface DocumentDiagnosticState {
52
+ readonly baselines: Readonly<Record<keyof WorkbenchAppearanceDiagnosticSnapshot, string | null>>;
53
+ readonly owners: DiagnosticOwner[];
54
+ readonly target: HTMLElement;
55
+ }
56
+
57
+ const documentDiagnosticStates = new WeakMap<HTMLElement, DocumentDiagnosticState>();
58
+
59
+ /**
60
+ * Copies only own data properties and freezes their canonically sanitized CSS values.
61
+ * Accessors are deliberately not evaluated at this compatibility boundary.
62
+ */
63
+ export function createWorkbenchAppearanceOverrideSnapshot(
64
+ tokenOverrides: Readonly<Record<string, string>> | undefined,
65
+ ): WorkbenchAppearanceOverrideSnapshot | undefined {
66
+ if (tokenOverrides === undefined) {
67
+ return undefined;
68
+ }
69
+
70
+ const snapshot: Record<string, string> = {};
71
+ for (const key of Object.keys(tokenOverrides)) {
72
+ if (!key.startsWith('--')) {
73
+ continue;
74
+ }
75
+ const descriptor = Object.getOwnPropertyDescriptor(tokenOverrides, key);
76
+ if (!descriptor || !('value' in descriptor) || typeof descriptor.value !== 'string') {
77
+ continue;
78
+ }
79
+
80
+ const sanitized = sanitizeThemeTokenValue(descriptor.value);
81
+ if (sanitized === null) {
82
+ continue;
83
+ }
84
+
85
+ Object.defineProperty(snapshot, key, {
86
+ configurable: true,
87
+ enumerable: true,
88
+ value: sanitized,
89
+ writable: true,
90
+ });
91
+ }
92
+
93
+ return Object.freeze(snapshot);
94
+ }
95
+
96
+ /**
97
+ * Registers one shell as a document legacy-override owner. The most recently registered live
98
+ * owner wins as one complete record; older cleanup cannot overwrite it. This coordinator owns
99
+ * CSS properties only and never reads or writes appearance attributes or descendants.
100
+ */
101
+ export function createWorkbenchDocumentAppearanceOverrideController(
102
+ target: HTMLElement,
103
+ initialTokenOverrides?: Readonly<Record<string, string>>,
104
+ ): WorkbenchDocumentAppearanceOverrideController {
105
+ const state = getOrCreateDocumentOverrideState(target);
106
+ const owner: OverrideOwner = {
107
+ generation: Symbol('workbench-appearance-override'),
108
+ snapshot: createWorkbenchAppearanceOverrideSnapshot(initialTokenOverrides),
109
+ };
110
+
111
+ captureBaselines(state, owner.snapshot);
112
+ state.owners.push(owner);
113
+ applyCurrentOwner(state);
114
+
115
+ let disposed = false;
116
+ return {
117
+ getSnapshot() {
118
+ return owner.snapshot;
119
+ },
120
+ update(tokenOverrides) {
121
+ if (disposed || !hasOwner(state, owner.generation)) {
122
+ return owner.snapshot;
123
+ }
124
+
125
+ const nextSnapshot = createWorkbenchAppearanceOverrideSnapshot(tokenOverrides);
126
+ captureBaselines(state, nextSnapshot);
127
+ owner.snapshot = nextSnapshot;
128
+
129
+ if (isCurrentOwner(state, owner.generation)) {
130
+ applyCurrentOwner(state);
131
+ }
132
+ releaseUnusedBaselines(state);
133
+ return owner.snapshot;
134
+ },
135
+ dispose() {
136
+ if (disposed) {
137
+ return;
138
+ }
139
+ disposed = true;
140
+
141
+ const ownerIndex = state.owners.findIndex(
142
+ (candidate) => candidate.generation === owner.generation,
143
+ );
144
+ if (ownerIndex < 0) {
145
+ return;
146
+ }
147
+
148
+ const wasCurrentOwner = ownerIndex === state.owners.length - 1;
149
+ state.owners.splice(ownerIndex, 1);
150
+ if (wasCurrentOwner) {
151
+ applyCurrentOwner(state);
152
+ }
153
+ releaseUnusedBaselines(state);
154
+
155
+ if (state.owners.length === 0) {
156
+ documentOverrideStates.delete(target);
157
+ }
158
+ },
159
+ };
160
+ }
161
+
162
+ /** Coordinates only private, unstyled unresolved-selection diagnostics on the document root. */
163
+ export function createWorkbenchDocumentAppearanceDiagnosticController(
164
+ target: HTMLElement,
165
+ initialSnapshot: WorkbenchAppearanceDiagnosticSnapshot = {},
166
+ ): WorkbenchDocumentAppearanceDiagnosticController {
167
+ const state = getOrCreateDocumentDiagnosticState(target);
168
+ const owner: DiagnosticOwner = {
169
+ generation: Symbol('workbench-appearance-diagnostic'),
170
+ snapshot: freezeDiagnosticSnapshot(initialSnapshot),
171
+ };
172
+ state.owners.push(owner);
173
+ applyCurrentDiagnosticOwner(state);
174
+
175
+ let disposed = false;
176
+ return {
177
+ update(snapshot) {
178
+ if (
179
+ disposed ||
180
+ !state.owners.some((candidate) => candidate.generation === owner.generation)
181
+ ) {
182
+ return;
183
+ }
184
+ owner.snapshot = freezeDiagnosticSnapshot(snapshot);
185
+ if (state.owners[state.owners.length - 1]?.generation === owner.generation) {
186
+ applyCurrentDiagnosticOwner(state);
187
+ }
188
+ },
189
+ dispose() {
190
+ if (disposed) {
191
+ return;
192
+ }
193
+ disposed = true;
194
+ const ownerIndex = state.owners.findIndex(
195
+ (candidate) => candidate.generation === owner.generation,
196
+ );
197
+ if (ownerIndex < 0) {
198
+ return;
199
+ }
200
+ const wasCurrent = ownerIndex === state.owners.length - 1;
201
+ state.owners.splice(ownerIndex, 1);
202
+ if (wasCurrent) {
203
+ applyCurrentDiagnosticOwner(state);
204
+ }
205
+ if (state.owners.length === 0) {
206
+ documentDiagnosticStates.delete(target);
207
+ }
208
+ },
209
+ };
210
+ }
211
+
212
+ function freezeDiagnosticSnapshot(
213
+ snapshot: WorkbenchAppearanceDiagnosticSnapshot,
214
+ ): WorkbenchAppearanceDiagnosticSnapshot {
215
+ return Object.freeze({
216
+ unresolvedTheme:
217
+ typeof snapshot.unresolvedTheme === 'string' ? snapshot.unresolvedTheme : undefined,
218
+ unresolvedThemePreset:
219
+ typeof snapshot.unresolvedThemePreset === 'string'
220
+ ? snapshot.unresolvedThemePreset
221
+ : undefined,
222
+ });
223
+ }
224
+
225
+ function getOrCreateDocumentDiagnosticState(target: HTMLElement): DocumentDiagnosticState {
226
+ const existing = documentDiagnosticStates.get(target);
227
+ if (existing) {
228
+ return existing;
229
+ }
230
+ const state: DocumentDiagnosticState = {
231
+ baselines: Object.freeze({
232
+ unresolvedTheme: target.getAttribute(APPEARANCE_DIAGNOSTIC_ATTRIBUTES.unresolvedTheme),
233
+ unresolvedThemePreset: target.getAttribute(
234
+ APPEARANCE_DIAGNOSTIC_ATTRIBUTES.unresolvedThemePreset,
235
+ ),
236
+ }),
237
+ owners: [],
238
+ target,
239
+ };
240
+ documentDiagnosticStates.set(target, state);
241
+ return state;
242
+ }
243
+
244
+ function applyCurrentDiagnosticOwner(state: DocumentDiagnosticState): void {
245
+ const current = state.owners[state.owners.length - 1]?.snapshot;
246
+ for (const key of Object.keys(APPEARANCE_DIAGNOSTIC_ATTRIBUTES) as Array<
247
+ keyof WorkbenchAppearanceDiagnosticSnapshot
248
+ >) {
249
+ const attribute = APPEARANCE_DIAGNOSTIC_ATTRIBUTES[key];
250
+ const value = current === undefined ? state.baselines[key] : current[key];
251
+ if (value === null || value === undefined) {
252
+ state.target.removeAttribute(attribute);
253
+ } else {
254
+ state.target.setAttribute(attribute, value);
255
+ }
256
+ }
257
+ }
258
+
259
+ function getOrCreateDocumentOverrideState(target: HTMLElement): DocumentOverrideState {
260
+ const existing = documentOverrideStates.get(target);
261
+ if (existing) {
262
+ return existing;
263
+ }
264
+
265
+ const state: DocumentOverrideState = {
266
+ baselines: new Map(),
267
+ owners: [],
268
+ target,
269
+ };
270
+ documentOverrideStates.set(target, state);
271
+ return state;
272
+ }
273
+
274
+ function captureBaselines(
275
+ state: DocumentOverrideState,
276
+ snapshot: WorkbenchAppearanceOverrideSnapshot | undefined,
277
+ ): void {
278
+ if (!snapshot) {
279
+ return;
280
+ }
281
+
282
+ for (const key of Object.keys(snapshot)) {
283
+ if (state.baselines.has(key)) {
284
+ continue;
285
+ }
286
+ state.baselines.set(key, {
287
+ priority: state.target.style.getPropertyPriority(key),
288
+ value: state.target.style.getPropertyValue(key),
289
+ });
290
+ }
291
+ }
292
+
293
+ function applyCurrentOwner(state: DocumentOverrideState): void {
294
+ for (const [key, baseline] of state.baselines) {
295
+ restoreProperty(state.target, key, baseline);
296
+ }
297
+
298
+ const currentOwner = state.owners[state.owners.length - 1];
299
+ applyThemeTokenOverrides(state.target, currentOwner?.snapshot);
300
+ }
301
+
302
+ function releaseUnusedBaselines(state: DocumentOverrideState): void {
303
+ for (const [key, baseline] of state.baselines) {
304
+ const isStillOwned = state.owners.some(
305
+ (owner) =>
306
+ owner.snapshot !== undefined && Object.prototype.hasOwnProperty.call(owner.snapshot, key),
307
+ );
308
+ if (isStillOwned) {
309
+ continue;
310
+ }
311
+
312
+ restoreProperty(state.target, key, baseline);
313
+ state.baselines.delete(key);
314
+ }
315
+ }
316
+
317
+ function restoreProperty(target: HTMLElement, key: string, baseline: PropertyBaseline): void {
318
+ if (baseline.value === '') {
319
+ target.style.removeProperty(key);
320
+ return;
321
+ }
322
+ target.style.setProperty(key, baseline.value, baseline.priority);
323
+ }
324
+
325
+ function hasOwner(state: DocumentOverrideState, generation: symbol): boolean {
326
+ return state.owners.some((owner) => owner.generation === generation);
327
+ }
328
+
329
+ function isCurrentOwner(state: DocumentOverrideState, generation: symbol): boolean {
330
+ return state.owners[state.owners.length - 1]?.generation === generation;
331
+ }
@@ -0,0 +1,269 @@
1
+ import type { ResolvedWorkbenchTheme } from '@workbench-kit/react/workbench';
2
+ import { REQUIRED_THEME_TOKEN_KEYS } from '@workbench-kit/workbench-core';
3
+
4
+ import {
5
+ resolveWorkbenchAppearanceSelection,
6
+ type WorkbenchAppearanceCatalogEntry,
7
+ type WorkbenchAppearanceCatalogSnapshot,
8
+ type WorkbenchAppearanceSelectionResolution,
9
+ type WorkbenchAppearanceSelectionTarget,
10
+ } from './appearance-catalog.js';
11
+ import {
12
+ createWorkbenchAppearanceOverrideSnapshot,
13
+ type WorkbenchAppearanceOverrideSnapshot,
14
+ } from './appearance-controller.js';
15
+
16
+ export type WorkbenchAppearancePresentationMode = 'flat-theme' | 'preset';
17
+ export type WorkbenchAppearancePresentationPreference = 'system' | 'light' | 'dark';
18
+
19
+ export type WorkbenchAppearanceThemeSelection =
20
+ | {
21
+ readonly kind: 'base-preference';
22
+ readonly preference: WorkbenchAppearancePresentationPreference;
23
+ readonly rawTheme: string | undefined;
24
+ }
25
+ | {
26
+ readonly kind: 'flat-theme';
27
+ readonly rawTheme: string;
28
+ };
29
+
30
+ export interface ResolveWorkbenchAppearancePresentationInput {
31
+ readonly catalog: WorkbenchAppearanceCatalogSnapshot;
32
+ readonly darkPreset?: string | undefined;
33
+ readonly lightPreset?: string | undefined;
34
+ readonly resolvedSystemTheme: ResolvedWorkbenchTheme;
35
+ readonly theme?: string | undefined;
36
+ }
37
+
38
+ export interface WorkbenchAppearancePresentationDecision {
39
+ readonly legacyTokenOverrides: WorkbenchAppearanceOverrideSnapshot | undefined;
40
+ readonly mode: WorkbenchAppearancePresentationMode;
41
+ readonly selectionResolution: WorkbenchAppearanceSelectionResolution | undefined;
42
+ readonly selectionTarget: WorkbenchAppearanceSelectionTarget | undefined;
43
+ readonly theme: string | undefined;
44
+ readonly themePreference: WorkbenchAppearancePresentationPreference | undefined;
45
+ readonly themePreset: string | undefined;
46
+ readonly unresolvedTheme: string | undefined;
47
+ readonly unresolvedThemePreset: string | undefined;
48
+ }
49
+
50
+ interface ResolvedBasePreference {
51
+ readonly preference: WorkbenchAppearancePresentationPreference;
52
+ readonly theme: ResolvedWorkbenchTheme;
53
+ }
54
+
55
+ interface DecisionInput {
56
+ readonly legacyTokenOverrides?: WorkbenchAppearanceOverrideSnapshot | undefined;
57
+ readonly mode: WorkbenchAppearancePresentationMode;
58
+ readonly selectionResolution?: WorkbenchAppearanceSelectionResolution | undefined;
59
+ readonly selectionTarget?: WorkbenchAppearanceSelectionTarget | undefined;
60
+ readonly theme?: string | undefined;
61
+ readonly themePreference?: WorkbenchAppearancePresentationPreference | undefined;
62
+ readonly themePreset?: string | undefined;
63
+ readonly unresolvedTheme?: string | undefined;
64
+ readonly unresolvedThemePreset?: string | undefined;
65
+ }
66
+
67
+ /** Projects raw controlled appearance values without mutating or recovering those values. */
68
+ export function resolveWorkbenchAppearancePresentation({
69
+ catalog,
70
+ darkPreset,
71
+ lightPreset,
72
+ resolvedSystemTheme,
73
+ theme,
74
+ }: ResolveWorkbenchAppearancePresentationInput): WorkbenchAppearancePresentationDecision {
75
+ const themeSelection = classifyWorkbenchAppearanceThemeSelection(theme);
76
+
77
+ if (lightPreset !== undefined && darkPreset !== undefined) {
78
+ if (themeSelection.kind === 'flat-theme') {
79
+ return createDecision({
80
+ mode: 'preset',
81
+ unresolvedTheme: themeSelection.rawTheme,
82
+ });
83
+ }
84
+ const basePreference = resolveBasePreference(themeSelection.preference, resolvedSystemTheme);
85
+ return resolvePresetPresentation(catalog, basePreference, lightPreset, darkPreset);
86
+ }
87
+
88
+ if (themeSelection.kind === 'base-preference') {
89
+ const basePreference = resolveBasePreference(themeSelection.preference, resolvedSystemTheme);
90
+ return createDecision({
91
+ mode: 'flat-theme',
92
+ theme: basePreference.theme,
93
+ themePreference: basePreference.preference,
94
+ });
95
+ }
96
+
97
+ return resolveFlatThemePresentation(catalog, themeSelection.rawTheme);
98
+ }
99
+
100
+ /** Shared package-private classification for controlled appearance values. */
101
+ export function classifyWorkbenchAppearanceThemeSelection(
102
+ theme: string | undefined,
103
+ ): WorkbenchAppearanceThemeSelection {
104
+ switch (theme) {
105
+ case undefined:
106
+ return Object.freeze({ kind: 'base-preference', preference: 'system', rawTheme: undefined });
107
+ case 'system':
108
+ case 'light':
109
+ case 'dark':
110
+ return Object.freeze({ kind: 'base-preference', preference: theme, rawTheme: theme });
111
+ default:
112
+ return Object.freeze({ kind: 'flat-theme', rawTheme: theme });
113
+ }
114
+ }
115
+
116
+ function resolveBasePreference(
117
+ preference: WorkbenchAppearancePresentationPreference,
118
+ resolvedSystemTheme: ResolvedWorkbenchTheme,
119
+ ): ResolvedBasePreference {
120
+ switch (preference) {
121
+ case 'system':
122
+ return { preference: 'system', theme: resolvedSystemTheme };
123
+ case 'light':
124
+ return { preference: 'light', theme: 'light' };
125
+ case 'dark':
126
+ return { preference: 'dark', theme: 'dark' };
127
+ }
128
+ }
129
+
130
+ function resolveFlatThemePresentation(
131
+ catalog: WorkbenchAppearanceCatalogSnapshot,
132
+ rawTheme: string,
133
+ ): WorkbenchAppearancePresentationDecision {
134
+ const selectionTarget = 'flat-theme';
135
+ const selectionResolution = resolveWorkbenchAppearanceSelection(
136
+ catalog,
137
+ selectionTarget,
138
+ rawTheme,
139
+ );
140
+
141
+ if (selectionResolution.status === 'resolved') {
142
+ if (selectionResolution.entry.source === 'host-option') {
143
+ return createDecision({
144
+ mode: 'flat-theme',
145
+ selectionResolution,
146
+ selectionTarget,
147
+ theme: rawTheme,
148
+ });
149
+ }
150
+
151
+ return createRegisteredThemeDecision({
152
+ entry: selectionResolution.entry,
153
+ mode: 'flat-theme',
154
+ selectionResolution,
155
+ selectionTarget,
156
+ });
157
+ }
158
+
159
+ if (selectionResolution.status === 'conflicted') {
160
+ return createDecision({
161
+ mode: 'flat-theme',
162
+ selectionResolution,
163
+ selectionTarget,
164
+ unresolvedTheme: rawTheme,
165
+ });
166
+ }
167
+
168
+ return createDecision({
169
+ mode: 'flat-theme',
170
+ selectionResolution,
171
+ selectionTarget,
172
+ theme: rawTheme,
173
+ });
174
+ }
175
+
176
+ function resolvePresetPresentation(
177
+ catalog: WorkbenchAppearanceCatalogSnapshot,
178
+ basePreference: ResolvedBasePreference,
179
+ lightPreset: string,
180
+ darkPreset: string,
181
+ ): WorkbenchAppearancePresentationDecision {
182
+ const selectionTarget =
183
+ basePreference.theme === 'light' ? ('light-preset' as const) : ('dark-preset' as const);
184
+ const rawPreset = basePreference.theme === 'light' ? lightPreset : darkPreset;
185
+ const selectionResolution = resolveWorkbenchAppearanceSelection(
186
+ catalog,
187
+ selectionTarget,
188
+ rawPreset,
189
+ );
190
+
191
+ if (selectionResolution.status !== 'resolved') {
192
+ return createDecision({
193
+ mode: 'preset',
194
+ selectionResolution,
195
+ selectionTarget,
196
+ theme: basePreference.theme,
197
+ themePreference: basePreference.preference,
198
+ unresolvedThemePreset: rawPreset,
199
+ });
200
+ }
201
+
202
+ if (selectionResolution.entry.source === 'builtin-preset') {
203
+ return createDecision({
204
+ mode: 'preset',
205
+ selectionResolution,
206
+ selectionTarget,
207
+ theme: basePreference.theme,
208
+ themePreference: basePreference.preference,
209
+ themePreset: rawPreset,
210
+ });
211
+ }
212
+
213
+ return createRegisteredThemeDecision({
214
+ entry: selectionResolution.entry,
215
+ mode: 'preset',
216
+ selectionResolution,
217
+ selectionTarget,
218
+ themePreference: basePreference.preference,
219
+ });
220
+ }
221
+
222
+ function createRegisteredThemeDecision({
223
+ entry,
224
+ mode,
225
+ selectionResolution,
226
+ selectionTarget,
227
+ themePreference,
228
+ }: {
229
+ readonly entry: WorkbenchAppearanceCatalogEntry;
230
+ readonly mode: WorkbenchAppearancePresentationMode;
231
+ readonly selectionResolution: WorkbenchAppearanceSelectionResolution;
232
+ readonly selectionTarget: WorkbenchAppearanceSelectionTarget;
233
+ readonly themePreference?: WorkbenchAppearancePresentationPreference | undefined;
234
+ }): WorkbenchAppearancePresentationDecision {
235
+ const legacyTokenOverrides = createWorkbenchAppearanceOverrideSnapshot(
236
+ entry.legacyTokenOverrides,
237
+ );
238
+ const completeLegacyTokenOverrides =
239
+ legacyTokenOverrides !== undefined &&
240
+ REQUIRED_THEME_TOKEN_KEYS.every((key) =>
241
+ Object.prototype.hasOwnProperty.call(legacyTokenOverrides, key),
242
+ )
243
+ ? legacyTokenOverrides
244
+ : undefined;
245
+
246
+ return createDecision({
247
+ legacyTokenOverrides: completeLegacyTokenOverrides,
248
+ mode,
249
+ selectionResolution,
250
+ selectionTarget,
251
+ theme: entry.mode,
252
+ themePreference,
253
+ themePreset: entry.id,
254
+ });
255
+ }
256
+
257
+ function createDecision(input: DecisionInput): WorkbenchAppearancePresentationDecision {
258
+ return Object.freeze({
259
+ legacyTokenOverrides: input.legacyTokenOverrides,
260
+ mode: input.mode,
261
+ selectionResolution: input.selectionResolution,
262
+ selectionTarget: input.selectionTarget,
263
+ theme: input.theme,
264
+ themePreference: input.themePreference,
265
+ themePreset: input.themePreset,
266
+ unresolvedTheme: input.unresolvedTheme,
267
+ unresolvedThemePreset: input.unresolvedThemePreset,
268
+ });
269
+ }