@workbench-kit/shell-react 0.0.2-prototype.0.2.41 → 0.0.2-prototype.0.2.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -2
- package/package.json +13 -10
- package/src/extensions/extension-enablement-controller.ts +40 -9
- package/src/extensions/theme-selection-protection.ts +80 -55
- package/src/field-remap/chrome-labels.ts +18 -0
- package/src/field-remap/convert-note-editor.tsx +30 -24
- package/src/field-remap/convert-palette.tsx +6 -0
- package/src/field-remap/demo.tsx +8 -2
- package/src/field-remap/detail-panel.tsx +225 -192
- package/src/field-remap/drag-payload.ts +48 -0
- package/src/field-remap/flow-adapter.ts +216 -88
- package/src/field-remap/flow-ops.ts +150 -0
- package/src/field-remap/flow.tsx +1055 -162
- package/src/field-remap/index.ts +2 -0
- package/src/field-remap/io-class-browse.tsx +34 -22
- package/src/field-remap/keyboard.ts +16 -0
- package/src/field-remap/modal-detail.tsx +34 -0
- package/src/field-remap/panel.tsx +50 -14
- package/src/field-remap/transform-options-editor.tsx +5 -1
- package/src/field-remap/view.css +61 -42
- package/src/index.ts +7 -0
- package/src/management/keybinding-overrides-storage.ts +48 -23
- package/src/management/keybinding-settings.tsx +10 -1
- package/src/management/use-keybinding-management.ts +69 -19
- package/src/shell/appearance-catalog.ts +453 -0
- package/src/shell/appearance-controller.ts +331 -0
- package/src/shell/appearance-presentation.ts +269 -0
- package/src/shell/provider.tsx +147 -18
- package/src/shell/settings.tsx +282 -153
- package/src/shell/shell.tsx +88 -18
- package/src/workbench/appearance-storage.ts +2 -2
- package/src/workbench/command-host-controller.tsx +223 -0
- package/src/workbench/command-host.tsx +100 -150
- package/src/workbench/keybinding-bridge.ts +84 -38
- package/src/workbench/shell-command-registration.ts +27 -2
package/src/shell/settings.tsx
CHANGED
|
@@ -1,32 +1,34 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
2
|
import { Badge, Checkbox, Field, Select } from '@workbench-kit/react/primitives';
|
|
3
3
|
import {
|
|
4
|
-
applyWorkbenchAppearance,
|
|
5
|
-
DARK_THEME_PRESET_OPTIONS,
|
|
6
4
|
DEFAULT_SHELL_PRESET,
|
|
7
|
-
LIGHT_THEME_PRESET_OPTIONS,
|
|
8
5
|
SHELL_PRESET_OPTIONS,
|
|
9
6
|
WORKBENCH_APPEARANCE_FIELD_DESCRIPTIONS,
|
|
10
7
|
WORKBENCH_APPEARANCE_FIELD_LABELS,
|
|
11
8
|
WORKBENCH_COLOR_SCHEME_OPTIONS,
|
|
12
|
-
type WorkbenchColorSchemePreference,
|
|
13
|
-
type WorkbenchThemePresetOption,
|
|
14
9
|
} from '@workbench-kit/react/workbench';
|
|
15
10
|
import {
|
|
16
11
|
WorkbenchSettingsSection,
|
|
17
12
|
type WorkbenchSettingsCategory,
|
|
18
13
|
} from '@workbench-kit/react/workbench/settings';
|
|
19
14
|
import {
|
|
20
|
-
applyThemeTokenOverrides,
|
|
21
15
|
type ConfigurationRegistry,
|
|
22
16
|
type LocalizationRegistry,
|
|
23
17
|
type PreferenceService,
|
|
24
|
-
type ThemeRegistry,
|
|
25
18
|
} from '@workbench-kit/workbench-core';
|
|
26
19
|
import type { PreferenceScope } from '@workbench-kit/workbench-config';
|
|
27
20
|
|
|
28
21
|
import { isRecord } from '../is-record.js';
|
|
29
|
-
import {
|
|
22
|
+
import type { WorkbenchExtensionCatalogReader } from './provider.js';
|
|
23
|
+
import {
|
|
24
|
+
getWorkbenchAppearanceCatalogEntries,
|
|
25
|
+
resolveWorkbenchAppearanceSelection,
|
|
26
|
+
type WorkbenchAppearanceCatalogEntry,
|
|
27
|
+
type WorkbenchAppearanceCatalogSnapshot,
|
|
28
|
+
type WorkbenchAppearanceSelectionResolution,
|
|
29
|
+
type WorkbenchAppearanceSelectionTarget,
|
|
30
|
+
} from './appearance-catalog.js';
|
|
31
|
+
import { classifyWorkbenchAppearanceThemeSelection } from './appearance-presentation.js';
|
|
30
32
|
import { WORKBENCH_PREFERENCE_SCOPES } from './settings-constants.js';
|
|
31
33
|
|
|
32
34
|
const APPEARANCE_SETTINGS_CATEGORY_ID = 'workbench.appearance';
|
|
@@ -37,6 +39,43 @@ export interface WorkbenchThemeOption {
|
|
|
37
39
|
label: string;
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
/** Captures one renderer-private own-data view shared by catalog and Settings projection. */
|
|
43
|
+
export function createWorkbenchThemeOptionSnapshot(
|
|
44
|
+
options: readonly WorkbenchThemeOption[] | undefined,
|
|
45
|
+
): readonly WorkbenchThemeOption[] | undefined {
|
|
46
|
+
if (options === undefined) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const snapshot = new Array<WorkbenchThemeOption>(options.length);
|
|
51
|
+
options.forEach((option, sourceOrdinal) => {
|
|
52
|
+
const idDescriptor = Object.getOwnPropertyDescriptor(option, 'id');
|
|
53
|
+
const labelDescriptor = Object.getOwnPropertyDescriptor(option, 'label');
|
|
54
|
+
if (
|
|
55
|
+
!idDescriptor ||
|
|
56
|
+
!('value' in idDescriptor) ||
|
|
57
|
+
typeof idDescriptor.value !== 'string' ||
|
|
58
|
+
!labelDescriptor ||
|
|
59
|
+
!('value' in labelDescriptor) ||
|
|
60
|
+
typeof labelDescriptor.value !== 'string'
|
|
61
|
+
) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const descriptionDescriptor = Object.getOwnPropertyDescriptor(option, 'description');
|
|
66
|
+
const captured = {
|
|
67
|
+
id: idDescriptor.value,
|
|
68
|
+
label: labelDescriptor.value,
|
|
69
|
+
...(descriptionDescriptor && 'value' in descriptionDescriptor
|
|
70
|
+
? { description: descriptionDescriptor.value as ReactNode }
|
|
71
|
+
: {}),
|
|
72
|
+
};
|
|
73
|
+
snapshot[sourceOrdinal] = Object.freeze(captured);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
return Object.freeze(snapshot);
|
|
77
|
+
}
|
|
78
|
+
|
|
40
79
|
export interface WorkbenchLocaleOption {
|
|
41
80
|
id: string;
|
|
42
81
|
label: string;
|
|
@@ -63,10 +102,10 @@ export interface WorkbenchSettingsCategoryInput extends WorkbenchAppearanceSetti
|
|
|
63
102
|
}
|
|
64
103
|
|
|
65
104
|
export interface WorkbenchSettingsContributionAccess {
|
|
105
|
+
readonly appearanceCatalog: WorkbenchAppearanceCatalogSnapshot;
|
|
66
106
|
readonly configurations: ConfigurationRegistry;
|
|
67
107
|
readonly extensionCatalog: WorkbenchExtensionCatalogReader;
|
|
68
108
|
readonly localizations: LocalizationRegistry;
|
|
69
|
-
readonly themes: ThemeRegistry;
|
|
70
109
|
}
|
|
71
110
|
|
|
72
111
|
export function createSettingsCategories(
|
|
@@ -88,9 +127,9 @@ export function createSettingsCategories(
|
|
|
88
127
|
}: WorkbenchSettingsCategoryInput,
|
|
89
128
|
): WorkbenchSettingsCategory[] {
|
|
90
129
|
const configurations = contributions.configurations.getConfigurations();
|
|
91
|
-
const mergedThemeOptions = mergeThemeOptions(themeOptions, contributions.themes.getThemes());
|
|
92
130
|
const localeOptions = buildLocaleOptions(contributions.localizations.getLocalizations());
|
|
93
131
|
const appearanceCategory = createAppearanceSettingsCategory({
|
|
132
|
+
appearanceCatalog: contributions.appearanceCatalog,
|
|
94
133
|
darkPreset,
|
|
95
134
|
lightPreset,
|
|
96
135
|
locale,
|
|
@@ -102,7 +141,7 @@ export function createSettingsCategories(
|
|
|
102
141
|
onThemeChange,
|
|
103
142
|
shellPreset,
|
|
104
143
|
theme,
|
|
105
|
-
themeOptions
|
|
144
|
+
themeOptions,
|
|
106
145
|
});
|
|
107
146
|
|
|
108
147
|
if (configurations.length === 0) {
|
|
@@ -171,33 +210,6 @@ function formatPreferenceScopeLabel(scope: PreferenceScope): string {
|
|
|
171
210
|
return WORKBENCH_PREFERENCE_SCOPES.find((candidate) => candidate.id === scope)?.label ?? scope;
|
|
172
211
|
}
|
|
173
212
|
|
|
174
|
-
function mergeThemeOptions(
|
|
175
|
-
baseOptions: readonly WorkbenchThemeOption[] | undefined,
|
|
176
|
-
contributedThemes: readonly {
|
|
177
|
-
id: string;
|
|
178
|
-
label: string;
|
|
179
|
-
tokenOverrides?: Record<string, string> | undefined;
|
|
180
|
-
}[],
|
|
181
|
-
): readonly WorkbenchThemeOption[] {
|
|
182
|
-
const merged = new Map<string, WorkbenchThemeOption>();
|
|
183
|
-
|
|
184
|
-
for (const option of baseOptions ?? []) {
|
|
185
|
-
merged.set(option.id, option);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
for (const theme of contributedThemes) {
|
|
189
|
-
merged.set(theme.id, {
|
|
190
|
-
description: theme.tokenOverrides
|
|
191
|
-
? 'Contributed theme with token overrides.'
|
|
192
|
-
: 'Contributed theme.',
|
|
193
|
-
id: theme.id,
|
|
194
|
-
label: theme.label,
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
return [...merged.values()];
|
|
199
|
-
}
|
|
200
|
-
|
|
201
213
|
function buildLocaleOptions(
|
|
202
214
|
localizations: readonly { locale: string; label: string }[],
|
|
203
215
|
): readonly WorkbenchLocaleOption[] {
|
|
@@ -210,7 +222,121 @@ function buildLocaleOptions(
|
|
|
210
222
|
return options;
|
|
211
223
|
}
|
|
212
224
|
|
|
225
|
+
interface WorkbenchAppearancePresentedOption extends WorkbenchThemeOption {
|
|
226
|
+
readonly entry: WorkbenchAppearanceCatalogEntry;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function projectAppearanceOptions(
|
|
230
|
+
catalog: WorkbenchAppearanceCatalogSnapshot,
|
|
231
|
+
target: WorkbenchAppearanceSelectionTarget,
|
|
232
|
+
hostOptions: readonly WorkbenchThemeOption[] | undefined,
|
|
233
|
+
): readonly WorkbenchAppearancePresentedOption[] {
|
|
234
|
+
const projected: WorkbenchAppearancePresentedOption[] = [];
|
|
235
|
+
const visitedIds = new Set<string>();
|
|
236
|
+
|
|
237
|
+
for (const entry of getWorkbenchAppearanceCatalogEntries(catalog, target)) {
|
|
238
|
+
if (visitedIds.has(entry.id)) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
visitedIds.add(entry.id);
|
|
242
|
+
|
|
243
|
+
const resolution = resolveWorkbenchAppearanceSelection(catalog, target, entry.id);
|
|
244
|
+
if (resolution.status !== 'resolved') {
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
let description: ReactNode;
|
|
249
|
+
if (entry.source === 'host-option') {
|
|
250
|
+
const original = hostOptions?.[entry.sourceOrdinal];
|
|
251
|
+
if (!original || original.id !== entry.id || original.label !== entry.label) {
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
description = original.description;
|
|
255
|
+
} else if (entry.source === 'legacy-extension-theme' || entry.source === 'legacy-host-theme') {
|
|
256
|
+
description = entry.hasLegacyCssOverrides
|
|
257
|
+
? 'Contributed theme that declares token overrides.'
|
|
258
|
+
: 'Contributed theme.';
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
projected.push({ description, entry, id: entry.id, label: entry.label });
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return projected;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function findProjectedOption(
|
|
268
|
+
options: readonly WorkbenchAppearancePresentedOption[],
|
|
269
|
+
id: string | undefined,
|
|
270
|
+
): WorkbenchAppearancePresentedOption | undefined {
|
|
271
|
+
return id === undefined ? undefined : options.find((option) => option.id === id);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function resolveRawAppearanceSelection(
|
|
275
|
+
catalog: WorkbenchAppearanceCatalogSnapshot,
|
|
276
|
+
target: WorkbenchAppearanceSelectionTarget,
|
|
277
|
+
rawValue: string | undefined,
|
|
278
|
+
): WorkbenchAppearanceSelectionResolution | undefined {
|
|
279
|
+
return rawValue === undefined
|
|
280
|
+
? undefined
|
|
281
|
+
: resolveWorkbenchAppearanceSelection(catalog, target, rawValue);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function describeAppearanceResolution(
|
|
285
|
+
rawValue: string,
|
|
286
|
+
resolution: Exclude<WorkbenchAppearanceSelectionResolution, { status: 'resolved' }>,
|
|
287
|
+
): string {
|
|
288
|
+
switch (resolution.status) {
|
|
289
|
+
case 'conflicted':
|
|
290
|
+
return `The appearance “${rawValue}” has conflicting sources. Choose another listed option to recover.`;
|
|
291
|
+
case 'wrong-scheme':
|
|
292
|
+
return `The appearance “${rawValue}” does not belong to the ${resolution.expected} scheme. Choose a listed option to recover.`;
|
|
293
|
+
case 'unresolved':
|
|
294
|
+
return `The appearance “${rawValue}” is unavailable. Choose a listed option to recover.`;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function renderInvalidAppearanceOption(
|
|
299
|
+
rawValue: string | undefined,
|
|
300
|
+
resolution: WorkbenchAppearanceSelectionResolution | undefined,
|
|
301
|
+
) {
|
|
302
|
+
if (rawValue === undefined) {
|
|
303
|
+
return (
|
|
304
|
+
<option disabled value="">
|
|
305
|
+
No appearance selected
|
|
306
|
+
</option>
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
if (!resolution || resolution.status === 'resolved') {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
return (
|
|
313
|
+
<option disabled value={rawValue}>
|
|
314
|
+
Unavailable: {rawValue}
|
|
315
|
+
</option>
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function AppearanceSelectionDiagnostic({
|
|
320
|
+
id,
|
|
321
|
+
rawValue,
|
|
322
|
+
resolution,
|
|
323
|
+
}: {
|
|
324
|
+
id: string;
|
|
325
|
+
rawValue: string | undefined;
|
|
326
|
+
resolution: WorkbenchAppearanceSelectionResolution | undefined;
|
|
327
|
+
}) {
|
|
328
|
+
if (rawValue === undefined || !resolution || resolution.status === 'resolved') {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
return (
|
|
332
|
+
<p id={id} className="workbench-appearance-settings__description" role="status">
|
|
333
|
+
{describeAppearanceResolution(rawValue, resolution)}
|
|
334
|
+
</p>
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
213
338
|
function createAppearanceSettingsCategory({
|
|
339
|
+
appearanceCatalog,
|
|
214
340
|
darkPreset,
|
|
215
341
|
lightPreset,
|
|
216
342
|
locale,
|
|
@@ -224,25 +350,20 @@ function createAppearanceSettingsCategory({
|
|
|
224
350
|
theme,
|
|
225
351
|
themeOptions,
|
|
226
352
|
}: WorkbenchAppearanceSettingsInput & {
|
|
353
|
+
appearanceCatalog: WorkbenchAppearanceCatalogSnapshot;
|
|
227
354
|
localeOptions: readonly WorkbenchLocaleOption[];
|
|
228
|
-
themeOptions: readonly WorkbenchThemeOption[];
|
|
229
355
|
}): WorkbenchSettingsCategory | undefined {
|
|
230
|
-
const usesAppearancePresets = lightPreset !== undefined && darkPreset !== undefined;
|
|
231
|
-
|
|
232
|
-
if (!usesAppearancePresets && !themeOptions?.length && !localeOptions?.length) {
|
|
233
|
-
return undefined;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
356
|
return {
|
|
237
357
|
content: (
|
|
238
358
|
<AppearanceSettingsSection
|
|
359
|
+
appearanceCatalog={appearanceCatalog}
|
|
239
360
|
darkPreset={darkPreset}
|
|
240
361
|
lightPreset={lightPreset}
|
|
241
362
|
locale={locale}
|
|
242
363
|
localeOptions={localeOptions ?? []}
|
|
243
364
|
shellPreset={shellPreset}
|
|
244
365
|
theme={theme}
|
|
245
|
-
themeOptions={themeOptions
|
|
366
|
+
themeOptions={themeOptions}
|
|
246
367
|
onDarkPresetChange={onDarkPresetChange}
|
|
247
368
|
onLightPresetChange={onLightPresetChange}
|
|
248
369
|
onLocaleChange={onLocaleChange}
|
|
@@ -256,6 +377,7 @@ function createAppearanceSettingsCategory({
|
|
|
256
377
|
}
|
|
257
378
|
|
|
258
379
|
function AppearanceSettingsSection({
|
|
380
|
+
appearanceCatalog,
|
|
259
381
|
darkPreset,
|
|
260
382
|
lightPreset,
|
|
261
383
|
locale,
|
|
@@ -269,119 +391,63 @@ function AppearanceSettingsSection({
|
|
|
269
391
|
theme,
|
|
270
392
|
themeOptions,
|
|
271
393
|
}: WorkbenchAppearanceSettingsInput & {
|
|
394
|
+
appearanceCatalog: WorkbenchAppearanceCatalogSnapshot;
|
|
272
395
|
localeOptions: readonly WorkbenchLocaleOption[];
|
|
273
|
-
themeOptions: readonly WorkbenchThemeOption[];
|
|
274
396
|
}) {
|
|
275
|
-
const { themes } = useWorkbench();
|
|
276
|
-
const themeRevision = themes.getRevision();
|
|
277
|
-
const containerRef = useRef<HTMLDivElement>(null);
|
|
278
|
-
const previousThemeOverridesRef = useRef<Readonly<Record<string, string>> | undefined>(undefined);
|
|
279
397
|
const usesAppearancePresets = lightPreset !== undefined && darkPreset !== undefined;
|
|
280
|
-
const
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
398
|
+
const themeSelection = classifyWorkbenchAppearanceThemeSelection(theme);
|
|
399
|
+
const flatThemeProjection = projectAppearanceOptions(
|
|
400
|
+
appearanceCatalog,
|
|
401
|
+
'flat-theme',
|
|
402
|
+
themeOptions,
|
|
403
|
+
);
|
|
404
|
+
const lightPresetProjection = projectAppearanceOptions(
|
|
405
|
+
appearanceCatalog,
|
|
406
|
+
'light-preset',
|
|
407
|
+
themeOptions,
|
|
408
|
+
);
|
|
409
|
+
const darkPresetProjection = projectAppearanceOptions(
|
|
410
|
+
appearanceCatalog,
|
|
411
|
+
'dark-preset',
|
|
412
|
+
themeOptions,
|
|
289
413
|
);
|
|
290
|
-
const
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
.getThemes()
|
|
295
|
-
.filter((contributedTheme) => contributedTheme.mode === 'dark')
|
|
296
|
-
.map((contributedTheme) => ({ id: contributedTheme.id, label: contributedTheme.label })),
|
|
297
|
-
],
|
|
298
|
-
[themeRevision, themes],
|
|
414
|
+
const flatThemeResolution = resolveRawAppearanceSelection(
|
|
415
|
+
appearanceCatalog,
|
|
416
|
+
'flat-theme',
|
|
417
|
+
themeSelection.kind === 'flat-theme' ? themeSelection.rawTheme : undefined,
|
|
299
418
|
);
|
|
300
|
-
const
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
419
|
+
const lightPresetResolution = resolveRawAppearanceSelection(
|
|
420
|
+
appearanceCatalog,
|
|
421
|
+
'light-preset',
|
|
422
|
+
lightPreset,
|
|
423
|
+
);
|
|
424
|
+
const darkPresetResolution = resolveRawAppearanceSelection(
|
|
425
|
+
appearanceCatalog,
|
|
426
|
+
'dark-preset',
|
|
427
|
+
darkPreset,
|
|
428
|
+
);
|
|
429
|
+
const selectedTheme = findProjectedOption(
|
|
430
|
+
flatThemeProjection,
|
|
431
|
+
themeSelection.kind === 'flat-theme' ? themeSelection.rawTheme : undefined,
|
|
432
|
+
);
|
|
433
|
+
const selectedThemeValue =
|
|
434
|
+
themeSelection.kind === 'base-preference' ? themeSelection.preference : themeSelection.rawTheme;
|
|
435
|
+
const colorSchemeDiagnostic =
|
|
436
|
+
usesAppearancePresets && themeSelection.kind === 'flat-theme'
|
|
437
|
+
? `The color scheme “${themeSelection.rawTheme}” is unavailable. Choose a listed scheme to recover.`
|
|
438
|
+
: undefined;
|
|
311
439
|
const selectedLocale = localeOptions.find((option) => option.id === locale) ?? localeOptions[0];
|
|
312
440
|
const selectedLocaleId = selectedLocale?.id ?? 'en';
|
|
313
441
|
const selectedShellPreset =
|
|
314
442
|
SHELL_PRESET_OPTIONS.find((option) => option.id === shellPreset) ?? SHELL_PRESET_OPTIONS[0];
|
|
315
443
|
|
|
316
|
-
useEffect(() => {
|
|
317
|
-
if (typeof document === 'undefined') {
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
let activeThemeId: string | undefined;
|
|
322
|
-
|
|
323
|
-
if (usesAppearancePresets) {
|
|
324
|
-
const appearanceSettings = {
|
|
325
|
-
darkPreset: selectedDarkPreset.id,
|
|
326
|
-
lightPreset: selectedLightPreset.id,
|
|
327
|
-
shellPreset: selectedShellPreset.id,
|
|
328
|
-
themePreference: selectedColorSchemeId,
|
|
329
|
-
};
|
|
330
|
-
const { resolvedTheme } = applyWorkbenchAppearance(
|
|
331
|
-
document.documentElement,
|
|
332
|
-
appearanceSettings,
|
|
333
|
-
);
|
|
334
|
-
activeThemeId = resolvedTheme === 'light' ? selectedLightPreset.id : selectedDarkPreset.id;
|
|
335
|
-
|
|
336
|
-
const workbenchRoot = containerRef.current?.closest<HTMLElement>(
|
|
337
|
-
'[data-theme-preset], [data-theme], [data-shell-preset]',
|
|
338
|
-
);
|
|
339
|
-
if (workbenchRoot && workbenchRoot !== document.documentElement) {
|
|
340
|
-
applyWorkbenchAppearance(workbenchRoot, appearanceSettings);
|
|
341
|
-
}
|
|
342
|
-
} else {
|
|
343
|
-
activeThemeId = selectedThemeId;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
const contributedTheme = activeThemeId ? themes.getTheme(activeThemeId) : undefined;
|
|
347
|
-
|
|
348
|
-
// The actual workbench root (e.g. `.ide-root`) re-declares `data-theme-preset` locally,
|
|
349
|
-
// which shadows inheritance from `documentElement` for everything rendered inside it.
|
|
350
|
-
// Apply the override there too, or it only ever reaches stray document.body portals.
|
|
351
|
-
const workbenchRoot = containerRef.current?.closest<HTMLElement>(
|
|
352
|
-
'[data-theme-preset], [data-theme]',
|
|
353
|
-
);
|
|
354
|
-
const overrideTargets =
|
|
355
|
-
workbenchRoot && workbenchRoot !== document.documentElement
|
|
356
|
-
? [document.documentElement, workbenchRoot]
|
|
357
|
-
: [document.documentElement];
|
|
358
|
-
|
|
359
|
-
for (const target of overrideTargets) {
|
|
360
|
-
applyThemeTokenOverrides(
|
|
361
|
-
target,
|
|
362
|
-
contributedTheme?.tokenOverrides,
|
|
363
|
-
previousThemeOverridesRef.current,
|
|
364
|
-
);
|
|
365
|
-
}
|
|
366
|
-
previousThemeOverridesRef.current = contributedTheme?.tokenOverrides;
|
|
367
|
-
}, [
|
|
368
|
-
themeRevision,
|
|
369
|
-
themes,
|
|
370
|
-
selectedColorSchemeId,
|
|
371
|
-
selectedDarkPreset.id,
|
|
372
|
-
selectedLightPreset.id,
|
|
373
|
-
selectedShellPreset.id,
|
|
374
|
-
selectedThemeId,
|
|
375
|
-
usesAppearancePresets,
|
|
376
|
-
]);
|
|
377
|
-
|
|
378
444
|
return (
|
|
379
445
|
<WorkbenchSettingsSection
|
|
380
446
|
id="workbench-settings-appearance"
|
|
381
447
|
title="Appearance"
|
|
382
448
|
description="Configure how the workbench is presented."
|
|
383
449
|
>
|
|
384
|
-
<div
|
|
450
|
+
<div className="workbench-appearance-settings">
|
|
385
451
|
{usesAppearancePresets ? (
|
|
386
452
|
<>
|
|
387
453
|
<Field
|
|
@@ -391,17 +457,34 @@ function AppearanceSettingsSection({
|
|
|
391
457
|
>
|
|
392
458
|
<Select
|
|
393
459
|
aria-label={WORKBENCH_APPEARANCE_FIELD_LABELS.colorScheme}
|
|
460
|
+
aria-describedby={
|
|
461
|
+
colorSchemeDiagnostic ? 'workbench-color-scheme-diagnostic' : undefined
|
|
462
|
+
}
|
|
394
463
|
controlWidth="full"
|
|
395
464
|
disabled={!onThemeChange}
|
|
396
|
-
value={
|
|
465
|
+
value={selectedThemeValue}
|
|
397
466
|
onValueChange={(nextTheme) => onThemeChange?.(nextTheme)}
|
|
398
467
|
>
|
|
468
|
+
{colorSchemeDiagnostic ? (
|
|
469
|
+
<option disabled value={theme}>
|
|
470
|
+
Unavailable: {theme}
|
|
471
|
+
</option>
|
|
472
|
+
) : null}
|
|
399
473
|
{WORKBENCH_COLOR_SCHEME_OPTIONS.map((option) => (
|
|
400
474
|
<option key={option.id} value={option.id}>
|
|
401
475
|
{option.label}
|
|
402
476
|
</option>
|
|
403
477
|
))}
|
|
404
478
|
</Select>
|
|
479
|
+
{colorSchemeDiagnostic ? (
|
|
480
|
+
<p
|
|
481
|
+
id="workbench-color-scheme-diagnostic"
|
|
482
|
+
className="workbench-appearance-settings__description"
|
|
483
|
+
role="status"
|
|
484
|
+
>
|
|
485
|
+
{colorSchemeDiagnostic}
|
|
486
|
+
</p>
|
|
487
|
+
) : null}
|
|
405
488
|
</Field>
|
|
406
489
|
<Field
|
|
407
490
|
className="workbench-appearance-settings__field"
|
|
@@ -410,17 +493,28 @@ function AppearanceSettingsSection({
|
|
|
410
493
|
>
|
|
411
494
|
<Select
|
|
412
495
|
aria-label={WORKBENCH_APPEARANCE_FIELD_LABELS.preferredLightColorTheme}
|
|
496
|
+
aria-describedby={
|
|
497
|
+
lightPresetResolution && lightPresetResolution.status !== 'resolved'
|
|
498
|
+
? 'workbench-light-preset-diagnostic'
|
|
499
|
+
: undefined
|
|
500
|
+
}
|
|
413
501
|
controlWidth="full"
|
|
414
502
|
disabled={!onLightPresetChange}
|
|
415
|
-
value={
|
|
503
|
+
value={lightPreset ?? ''}
|
|
416
504
|
onValueChange={(nextPreset) => onLightPresetChange?.(nextPreset)}
|
|
417
505
|
>
|
|
418
|
-
{
|
|
506
|
+
{renderInvalidAppearanceOption(lightPreset, lightPresetResolution)}
|
|
507
|
+
{lightPresetProjection.map((option) => (
|
|
419
508
|
<option key={option.id} value={option.id}>
|
|
420
509
|
{option.label}
|
|
421
510
|
</option>
|
|
422
511
|
))}
|
|
423
512
|
</Select>
|
|
513
|
+
<AppearanceSelectionDiagnostic
|
|
514
|
+
id="workbench-light-preset-diagnostic"
|
|
515
|
+
rawValue={lightPreset}
|
|
516
|
+
resolution={lightPresetResolution}
|
|
517
|
+
/>
|
|
424
518
|
</Field>
|
|
425
519
|
<Field
|
|
426
520
|
className="workbench-appearance-settings__field"
|
|
@@ -429,17 +523,28 @@ function AppearanceSettingsSection({
|
|
|
429
523
|
>
|
|
430
524
|
<Select
|
|
431
525
|
aria-label={WORKBENCH_APPEARANCE_FIELD_LABELS.preferredDarkColorTheme}
|
|
526
|
+
aria-describedby={
|
|
527
|
+
darkPresetResolution && darkPresetResolution.status !== 'resolved'
|
|
528
|
+
? 'workbench-dark-preset-diagnostic'
|
|
529
|
+
: undefined
|
|
530
|
+
}
|
|
432
531
|
controlWidth="full"
|
|
433
532
|
disabled={!onDarkPresetChange}
|
|
434
|
-
value={
|
|
533
|
+
value={darkPreset ?? ''}
|
|
435
534
|
onValueChange={(nextPreset) => onDarkPresetChange?.(nextPreset)}
|
|
436
535
|
>
|
|
437
|
-
{
|
|
536
|
+
{renderInvalidAppearanceOption(darkPreset, darkPresetResolution)}
|
|
537
|
+
{darkPresetProjection.map((option) => (
|
|
438
538
|
<option key={option.id} value={option.id}>
|
|
439
539
|
{option.label}
|
|
440
540
|
</option>
|
|
441
541
|
))}
|
|
442
542
|
</Select>
|
|
543
|
+
<AppearanceSelectionDiagnostic
|
|
544
|
+
id="workbench-dark-preset-diagnostic"
|
|
545
|
+
rawValue={darkPreset}
|
|
546
|
+
resolution={darkPresetResolution}
|
|
547
|
+
/>
|
|
443
548
|
</Field>
|
|
444
549
|
<Field
|
|
445
550
|
className="workbench-appearance-settings__field"
|
|
@@ -461,7 +566,7 @@ function AppearanceSettingsSection({
|
|
|
461
566
|
</Select>
|
|
462
567
|
</Field>
|
|
463
568
|
</>
|
|
464
|
-
) :
|
|
569
|
+
) : (
|
|
465
570
|
<Field
|
|
466
571
|
className="workbench-appearance-settings__field"
|
|
467
572
|
label="Color theme"
|
|
@@ -469,24 +574,48 @@ function AppearanceSettingsSection({
|
|
|
469
574
|
>
|
|
470
575
|
<Select
|
|
471
576
|
aria-label="Color theme"
|
|
577
|
+
aria-describedby={
|
|
578
|
+
flatThemeResolution && flatThemeResolution.status !== 'resolved'
|
|
579
|
+
? 'workbench-flat-theme-diagnostic'
|
|
580
|
+
: undefined
|
|
581
|
+
}
|
|
472
582
|
controlWidth="full"
|
|
473
583
|
disabled={!onThemeChange}
|
|
474
|
-
value={
|
|
584
|
+
value={selectedThemeValue}
|
|
475
585
|
onValueChange={(nextTheme) => onThemeChange?.(nextTheme)}
|
|
476
586
|
>
|
|
477
|
-
{
|
|
587
|
+
{renderInvalidAppearanceOption(
|
|
588
|
+
themeSelection.kind === 'flat-theme' ? themeSelection.rawTheme : undefined,
|
|
589
|
+
flatThemeResolution,
|
|
590
|
+
)}
|
|
591
|
+
{WORKBENCH_COLOR_SCHEME_OPTIONS.map((option) => (
|
|
478
592
|
<option key={option.id} value={option.id}>
|
|
479
593
|
{option.label}
|
|
480
594
|
</option>
|
|
481
595
|
))}
|
|
596
|
+
{flatThemeProjection
|
|
597
|
+
.filter(
|
|
598
|
+
(option) =>
|
|
599
|
+
classifyWorkbenchAppearanceThemeSelection(option.id).kind === 'flat-theme',
|
|
600
|
+
)
|
|
601
|
+
.map((option) => (
|
|
602
|
+
<option key={option.id} value={option.id}>
|
|
603
|
+
{option.label}
|
|
604
|
+
</option>
|
|
605
|
+
))}
|
|
482
606
|
</Select>
|
|
483
607
|
{selectedTheme?.description ? (
|
|
484
608
|
<p className="workbench-appearance-settings__description">
|
|
485
609
|
{selectedTheme.description}
|
|
486
610
|
</p>
|
|
487
611
|
) : null}
|
|
612
|
+
<AppearanceSelectionDiagnostic
|
|
613
|
+
id="workbench-flat-theme-diagnostic"
|
|
614
|
+
rawValue={themeSelection.kind === 'flat-theme' ? themeSelection.rawTheme : undefined}
|
|
615
|
+
resolution={flatThemeResolution}
|
|
616
|
+
/>
|
|
488
617
|
</Field>
|
|
489
|
-
)
|
|
618
|
+
)}
|
|
490
619
|
{localeOptions.length > 1 ? (
|
|
491
620
|
<Field
|
|
492
621
|
className="workbench-appearance-settings__field"
|