@opensumi/ide-theme 2.21.13 → 2.22.0

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 (117) hide show
  1. package/lib/browser/icon-theme-data.js.map +1 -1
  2. package/lib/browser/icon-theme-store.js.map +1 -1
  3. package/lib/browser/icon.service.d.ts +3 -1
  4. package/lib/browser/icon.service.d.ts.map +1 -1
  5. package/lib/browser/icon.service.js +56 -24
  6. package/lib/browser/icon.service.js.map +1 -1
  7. package/lib/browser/index.js.map +1 -1
  8. package/lib/browser/semantic-tokens-registry.js.map +1 -1
  9. package/lib/browser/style.service.js.map +1 -1
  10. package/lib/browser/theme-data.js +7 -7
  11. package/lib/browser/theme-data.js.map +1 -1
  12. package/lib/browser/theme-store.js.map +1 -1
  13. package/lib/browser/theme.contribution.js +3 -3
  14. package/lib/browser/theme.contribution.js.map +1 -1
  15. package/lib/browser/workbench.theme.service.js +2 -2
  16. package/lib/browser/workbench.theme.service.js.map +1 -1
  17. package/lib/common/color-tokens/basic-color.d.ts +1 -1
  18. package/lib/common/color-tokens/basic-color.d.ts.map +1 -1
  19. package/lib/common/color-tokens/editor.d.ts +13 -0
  20. package/lib/common/color-tokens/editor.d.ts.map +1 -1
  21. package/lib/common/color-tokens/editor.js +35 -1
  22. package/lib/common/color-tokens/editor.js.map +1 -1
  23. package/lib/common/color.js +40 -40
  24. package/lib/common/color.js.map +1 -1
  25. package/lib/common/mocks/theme.service.js.map +1 -1
  26. package/lib/common/plistParser.js +40 -40
  27. package/lib/common/plistParser.js.map +1 -1
  28. package/lib/common/semantic-tokens-registry.d.ts +7 -7
  29. package/lib/common/semantic-tokens-registry.d.ts.map +1 -1
  30. package/lib/common/theme.service.d.ts +26 -6
  31. package/lib/common/theme.service.d.ts.map +1 -1
  32. package/lib/common/theme.service.js.map +1 -1
  33. package/package.json +11 -10
  34. package/src/browser/default-theme.ts +547 -0
  35. package/src/browser/icon-theme-data.ts +294 -0
  36. package/src/browser/icon-theme-store.ts +38 -0
  37. package/src/browser/icon.less +15 -0
  38. package/src/browser/icon.service.ts +457 -0
  39. package/src/browser/index.ts +45 -0
  40. package/src/browser/semantic-tokens-registry.ts +217 -0
  41. package/src/browser/style.service.ts +51 -0
  42. package/src/browser/theme-data.ts +719 -0
  43. package/src/browser/theme-store.ts +95 -0
  44. package/src/browser/theme.contribution.ts +343 -0
  45. package/src/browser/workbench.theme.service.ts +703 -0
  46. package/src/common/color-registry.ts +52 -0
  47. package/src/common/color-tokens/activity-bar.ts +122 -0
  48. package/src/common/color-tokens/badge.ts +31 -0
  49. package/src/common/color-tokens/base.ts +90 -0
  50. package/src/common/color-tokens/basic-color.ts +9 -0
  51. package/src/common/color-tokens/breadcrumb.ts +60 -0
  52. package/src/common/color-tokens/button.ts +69 -0
  53. package/src/common/color-tokens/charts.ts +68 -0
  54. package/src/common/color-tokens/checkbox.ts +23 -0
  55. package/src/common/color-tokens/custom/actionbar.ts +51 -0
  56. package/src/common/color-tokens/custom/activity-bar.ts +16 -0
  57. package/src/common/color-tokens/custom/badge.ts +30 -0
  58. package/src/common/color-tokens/custom/base.ts +111 -0
  59. package/src/common/color-tokens/custom/button.ts +359 -0
  60. package/src/common/color-tokens/custom/checkbox.ts +36 -0
  61. package/src/common/color-tokens/custom/decoration.ts +71 -0
  62. package/src/common/color-tokens/custom/editor.ts +27 -0
  63. package/src/common/color-tokens/custom/extension.ts +9 -0
  64. package/src/common/color-tokens/custom/icon.ts +30 -0
  65. package/src/common/color-tokens/custom/index.ts +26 -0
  66. package/src/common/color-tokens/custom/input.ts +48 -0
  67. package/src/common/color-tokens/custom/menu.ts +61 -0
  68. package/src/common/color-tokens/custom/modal.ts +57 -0
  69. package/src/common/color-tokens/custom/notification.ts +16 -0
  70. package/src/common/color-tokens/custom/panel.ts +112 -0
  71. package/src/common/color-tokens/custom/popover.ts +28 -0
  72. package/src/common/color-tokens/custom/select.ts +155 -0
  73. package/src/common/color-tokens/custom/settings.ts +32 -0
  74. package/src/common/color-tokens/custom/statusbar.ts +16 -0
  75. package/src/common/color-tokens/custom/tab.ts +31 -0
  76. package/src/common/color-tokens/custom/tooltip.ts +55 -0
  77. package/src/common/color-tokens/custom/tree.ts +106 -0
  78. package/src/common/color-tokens/debug.ts +103 -0
  79. package/src/common/color-tokens/debugToolbar.ts +134 -0
  80. package/src/common/color-tokens/dropdown.ts +27 -0
  81. package/src/common/color-tokens/editor.ts +945 -0
  82. package/src/common/color-tokens/index.ts +35 -0
  83. package/src/common/color-tokens/input.ts +105 -0
  84. package/src/common/color-tokens/list-tree.ts +205 -0
  85. package/src/common/color-tokens/menu-bar.ts +43 -0
  86. package/src/common/color-tokens/menu.ts +53 -0
  87. package/src/common/color-tokens/merge-conflict.ts +145 -0
  88. package/src/common/color-tokens/minimap.ts +99 -0
  89. package/src/common/color-tokens/notification.ts +169 -0
  90. package/src/common/color-tokens/panel.ts +177 -0
  91. package/src/common/color-tokens/pick-view.ts +96 -0
  92. package/src/common/color-tokens/picker.ts +15 -0
  93. package/src/common/color-tokens/progress-bar.ts +12 -0
  94. package/src/common/color-tokens/quick-input.ts +57 -0
  95. package/src/common/color-tokens/scrollbar.ts +42 -0
  96. package/src/common/color-tokens/settings.ts +126 -0
  97. package/src/common/color-tokens/sidebar.ts +121 -0
  98. package/src/common/color-tokens/snippet.ts +33 -0
  99. package/src/common/color-tokens/status-bar.ts +350 -0
  100. package/src/common/color-tokens/tab.ts +346 -0
  101. package/src/common/color-tokens/testing.ts +105 -0
  102. package/src/common/color-tokens/text.ts +41 -0
  103. package/src/common/color-tokens/title-bar.ts +62 -0
  104. package/src/common/color-tokens/toolbar.ts +28 -0
  105. package/src/common/color-tokens/welcome-page.ts +27 -0
  106. package/src/common/color.ts +647 -0
  107. package/src/common/default-themes.ts +273 -0
  108. package/src/common/event.ts +9 -0
  109. package/src/common/index.ts +8 -0
  110. package/src/common/mocks/theme.service.ts +55 -0
  111. package/src/common/plistParser.ts +525 -0
  112. package/src/common/semantic-tokens-registry.ts +439 -0
  113. package/src/common/style.ts +9 -0
  114. package/src/common/theme.service.ts +363 -0
  115. package/src/common/themeCompatibility.ts +95 -0
  116. package/src/common/utils.ts +195 -0
  117. package/src/index.ts +1 -0
@@ -0,0 +1,95 @@
1
+ import { Injectable, Autowired, INJECTOR_TOKEN, Injector } from '@opensumi/di';
2
+ import { URI, getDebugLogger } from '@opensumi/ide-core-common';
3
+
4
+ import { ThemeContribution, getThemeId } from '../common/theme.service';
5
+
6
+ import defaultTheme from './default-theme';
7
+ import { ThemeData } from './theme-data';
8
+
9
+ @Injectable()
10
+ export class ThemeStore {
11
+ static STORE_THEME_DATA_KEY = 'latestTheme';
12
+
13
+ private themes: {
14
+ [themeId: string]: ThemeData;
15
+ } = {};
16
+
17
+ @Autowired(INJECTOR_TOKEN)
18
+ injector: Injector;
19
+
20
+ protected async initTheme(contribution: ThemeContribution, extPath: URI): Promise<ThemeData> {
21
+ const themePath = contribution.path.replace(/^\.\//, '');
22
+ const themeLocation = extPath.resolve(themePath);
23
+ const themeName = contribution.label;
24
+ const themeId = getThemeId(contribution);
25
+ const themeBase = contribution.uiTheme || 'vs-dark';
26
+ await this.initThemeData(themeId, themeName, themeBase, themeLocation);
27
+ return this.themes[themeId];
28
+ }
29
+
30
+ private async initThemeData(id: string, themeName: string, themeBase: string, themeLocation: URI) {
31
+ let themeData = this.themes[id];
32
+ if (!themeData) {
33
+ themeData = this.injector.get(ThemeData);
34
+ await themeData.initializeThemeData(id, themeName, themeBase, themeLocation);
35
+ this.themes[id] = themeData;
36
+ }
37
+ }
38
+
39
+ loadDefaultTheme() {
40
+ getDebugLogger().warn('The default theme extension is not detected, and the default theme style is used.');
41
+ const theme = this.injector.get(ThemeData);
42
+ theme.initializeFromData(defaultTheme);
43
+ return theme;
44
+ }
45
+
46
+ private async tryLoadLatestTheme() {
47
+ // 尝试使用最近一次缓存的主题信息进行加载
48
+ const themeData = this.getLatestThemeData();
49
+ if (themeData) {
50
+ try {
51
+ const { contribution, basePath } = themeData;
52
+ const theme = await this.initTheme(contribution, new URI(basePath));
53
+ return theme;
54
+ } catch (e) {
55
+ // 主题不存在或被卸载时,移除缓存,使用默认样式
56
+ localStorage.removeItem(ThemeStore.STORE_THEME_DATA_KEY);
57
+ }
58
+ }
59
+ return this.loadDefaultTheme();
60
+ }
61
+
62
+ private storeLatestThemeData(contribution?: ThemeContribution, basePath?: URI) {
63
+ localStorage.setItem(
64
+ ThemeStore.STORE_THEME_DATA_KEY,
65
+ JSON.stringify({ contribution, basePath: basePath?.toString() }),
66
+ );
67
+ }
68
+
69
+ private getLatestThemeData() {
70
+ const data = localStorage.getItem(ThemeStore.STORE_THEME_DATA_KEY);
71
+ if (data) {
72
+ return JSON.parse(data);
73
+ }
74
+ }
75
+
76
+ public async getThemeData(contribution?: ThemeContribution, basePath?: URI): Promise<ThemeData> {
77
+ // 测试情况下传入的contribution为空,尝试加载上次缓存的最新主题
78
+ if (!contribution || !basePath) {
79
+ return await this.tryLoadLatestTheme();
80
+ }
81
+ const id = getThemeId(contribution);
82
+ if (!this.themes[id]) {
83
+ const theme = await this.initTheme(contribution, basePath);
84
+ if (theme) {
85
+ // 正常加载主题
86
+ this.storeLatestThemeData(contribution, basePath);
87
+ return theme;
88
+ }
89
+ // 加载主题出现了未知问题, 使用默认主题配色
90
+ return this.loadDefaultTheme();
91
+ }
92
+ // 主题有缓存
93
+ return this.themes[id];
94
+ }
95
+ }
@@ -0,0 +1,343 @@
1
+ import { Autowired } from '@opensumi/di';
2
+ import {
3
+ Domain,
4
+ CommandContribution,
5
+ CommandRegistry,
6
+ Command,
7
+ localize,
8
+ PreferenceService,
9
+ replaceLocalizePlaceholder,
10
+ PreferenceScope,
11
+ QuickOpenService,
12
+ QuickOpenOptions,
13
+ QuickOpenItem,
14
+ Mode,
15
+ ClientAppContribution,
16
+ GeneralSettingsId,
17
+ } from '@opensumi/ide-core-browser';
18
+ import { MenuContribution, IMenuRegistry, MenuId } from '@opensumi/ide-core-browser/lib/menu/next';
19
+
20
+ import {
21
+ IThemeService,
22
+ IIconService,
23
+ BuiltinThemeComparator,
24
+ getThemeTypeName,
25
+ BuiltinTheme,
26
+ DEFAULT_THEME_ID,
27
+ } from '../common';
28
+ import { ISemanticTokenRegistry, ProbeScope } from '../common/semantic-tokens-registry';
29
+
30
+ export const THEME_TOGGLE_COMMAND: Command = {
31
+ id: 'theme.toggle',
32
+ label: '%theme.toggle%',
33
+ alias: 'Color Theme',
34
+ };
35
+
36
+ export const ICON_THEME_TOGGLE_COMMAND: Command = {
37
+ id: 'theme.icon.toggle',
38
+ label: '%theme.icon.toggle%',
39
+ alias: 'File Icon Theme',
40
+ };
41
+
42
+ @Domain(MenuContribution, CommandContribution, ClientAppContribution)
43
+ export class ThemeContribution implements MenuContribution, CommandContribution, ClientAppContribution {
44
+ @Autowired(IThemeService)
45
+ themeService: IThemeService;
46
+
47
+ @Autowired(IIconService)
48
+ iconService: IIconService;
49
+
50
+ @Autowired(QuickOpenService)
51
+ private quickOpenService: QuickOpenService;
52
+
53
+ @Autowired(PreferenceService)
54
+ private preferenceService: PreferenceService;
55
+
56
+ @Autowired(ISemanticTokenRegistry)
57
+ protected readonly semanticTokenRegistry: ISemanticTokenRegistry;
58
+
59
+ async initialize() {
60
+ this.registerDefaultColorTheme();
61
+ this.registerDefaultTokenStyles();
62
+ this.registerDefaultTokenType();
63
+ this.registerDefaultTokenModifier();
64
+ await this.themeService.colorThemeLoaded.promise;
65
+ }
66
+
67
+ /**
68
+ * 如果没有设置默认 theme 或者 设置的 theme 为 dark 类型,为了有体感上的加速,设置默认的 theme
69
+ */
70
+ private registerDefaultColorTheme() {
71
+ const themeId = this.preferenceService.get<string>('general.theme');
72
+ const shouldApplyDefaultThemeId = !themeId || themeId.includes('dark');
73
+
74
+ if (shouldApplyDefaultThemeId) {
75
+ this.themeService.applyTheme(DEFAULT_THEME_ID);
76
+ }
77
+ }
78
+
79
+ private registerDefaultTokenModifier() {
80
+ this.semanticTokenRegistry.registerTokenModifier(
81
+ 'declaration',
82
+ localize('declaration', 'Style for all symbol declarations.'),
83
+ undefined,
84
+ );
85
+ this.semanticTokenRegistry.registerTokenModifier(
86
+ 'documentation',
87
+ localize('documentation', 'Style to use for references in documentation.'),
88
+ undefined,
89
+ );
90
+ this.semanticTokenRegistry.registerTokenModifier(
91
+ 'static',
92
+ localize('static', 'Style to use for symbols that are static.'),
93
+ undefined,
94
+ );
95
+ this.semanticTokenRegistry.registerTokenModifier(
96
+ 'abstract',
97
+ localize('abstract', 'Style to use for symbols that are abstract.'),
98
+ undefined,
99
+ );
100
+ this.semanticTokenRegistry.registerTokenModifier(
101
+ 'deprecated',
102
+ localize('deprecated', 'Style to use for symbols that are deprecated.'),
103
+ undefined,
104
+ );
105
+ this.semanticTokenRegistry.registerTokenModifier(
106
+ 'modification',
107
+ localize('modification', 'Style to use for write accesses.'),
108
+ undefined,
109
+ );
110
+ this.semanticTokenRegistry.registerTokenModifier(
111
+ 'async',
112
+ localize('async', 'Style to use for symbols that are async.'),
113
+ undefined,
114
+ );
115
+ this.semanticTokenRegistry.registerTokenModifier(
116
+ 'readonly',
117
+ localize('readonly', 'Style to use for symbols that are readonly.'),
118
+ undefined,
119
+ );
120
+ }
121
+
122
+ private registerDefaultTokenType() {
123
+ this.doRegisterTokenType('comment', localize('comment', 'Style for comments.'), [['comment']]);
124
+ this.doRegisterTokenType('string', localize('string', 'Style for strings.'), [['string']]);
125
+ this.doRegisterTokenType('keyword', localize('keyword', 'Style for keywords.'), [['keyword.control']]);
126
+ this.doRegisterTokenType('number', localize('number', 'Style for numbers.'), [['constant.numeric']]);
127
+ this.doRegisterTokenType('regexp', localize('regexp', 'Style for expressions.'), [['constant.regexp']]);
128
+ this.doRegisterTokenType('operator', localize('operator', 'Style for operators.'), [['keyword.operator']]);
129
+
130
+ this.doRegisterTokenType('namespace', localize('namespace', 'Style for namespaces.'), [['entity.name.namespace']]);
131
+
132
+ this.doRegisterTokenType('type', localize('type', 'Style for types.'), [['entity.name.type'], ['support.type']]);
133
+ this.doRegisterTokenType('struct', localize('struct', 'Style for structs.'), [['entity.name.type.struct']]);
134
+ this.doRegisterTokenType('class', localize('class', 'Style for classes.'), [
135
+ ['entity.name.type.class'],
136
+ ['support.class'],
137
+ ]);
138
+ this.doRegisterTokenType('interface', localize('interface', 'Style for interfaces.'), [
139
+ ['entity.name.type.interface'],
140
+ ]);
141
+ this.doRegisterTokenType('enum', localize('enum', 'Style for enums.'), [['entity.name.type.enum']]);
142
+ this.doRegisterTokenType('typeParameter', localize('typeParameter', 'Style for type parameters.'), [
143
+ ['entity.name.type.parameter'],
144
+ ]);
145
+
146
+ this.doRegisterTokenType('function', localize('function', 'Style for functions'), [
147
+ ['entity.name.function'],
148
+ ['support.function'],
149
+ ]);
150
+ this.doRegisterTokenType(
151
+ 'member',
152
+ localize('member', 'Style for member functions'),
153
+ [],
154
+ 'method',
155
+ 'Deprecated use `method` instead',
156
+ );
157
+ this.doRegisterTokenType('method', localize('method', 'Style for method (member functions)'), [
158
+ ['entity.name.function.member'],
159
+ ['support.function'],
160
+ ]);
161
+ this.doRegisterTokenType('macro', localize('macro', 'Style for macros.'), [['entity.name.function.preprocessor']]);
162
+
163
+ this.doRegisterTokenType('variable', localize('variable', 'Style for variables.'), [
164
+ ['variable.other.readwrite'],
165
+ ['entity.name.variable'],
166
+ ]);
167
+ this.doRegisterTokenType('parameter', localize('parameter', 'Style for parameters.'), [['variable.parameter']]);
168
+ this.doRegisterTokenType('property', localize('property', 'Style for properties.'), [['variable.other.property']]);
169
+ this.doRegisterTokenType('enumMember', localize('enumMember', 'Style for enum members.'), [
170
+ ['variable.other.enummember'],
171
+ ]);
172
+ this.doRegisterTokenType('event', localize('event', 'Style for events.'), [['variable.other.event']]);
173
+
174
+ this.doRegisterTokenType('label', localize('labels', 'Style for labels. '), undefined);
175
+ }
176
+
177
+ private registerDefaultTokenStyles() {
178
+ this.doRegisterTokenStyleDefault('variable.readonly', [['variable.other.constant']]);
179
+ this.doRegisterTokenStyleDefault('property.readonly', [['variable.other.constant.property']]);
180
+ this.doRegisterTokenStyleDefault('type.defaultLibrary', [['support.type']]);
181
+ this.doRegisterTokenStyleDefault('class.defaultLibrary', [['support.class']]);
182
+ this.doRegisterTokenStyleDefault('interface.defaultLibrary', [['support.class']]);
183
+ this.doRegisterTokenStyleDefault('variable.defaultLibrary', [['support.variable'], ['support.other.variable']]);
184
+ this.doRegisterTokenStyleDefault('variable.defaultLibrary.readonly', [['support.constant']]);
185
+ this.doRegisterTokenStyleDefault('property.defaultLibrary', [['support.variable.property']]);
186
+ this.doRegisterTokenStyleDefault('property.defaultLibrary.readonly', [['support.constant.property']]);
187
+ this.doRegisterTokenStyleDefault('function.defaultLibrary', [['support.function']]);
188
+ this.doRegisterTokenStyleDefault('member.defaultLibrary', [['support.function']]);
189
+ }
190
+
191
+ private doRegisterTokenStyleDefault(selectorString: string, scopesToProbe: ProbeScope[]) {
192
+ try {
193
+ const selector = this.semanticTokenRegistry.parseTokenSelector(selectorString);
194
+ this.semanticTokenRegistry.registerTokenStyleDefault(selector, { scopesToProbe });
195
+ } catch (e) {
196
+ // ignore error
197
+ }
198
+ }
199
+
200
+ private doRegisterTokenType(
201
+ id: string,
202
+ description: string,
203
+ scopesToProbe: ProbeScope[] = [],
204
+ superType?: string,
205
+ deprecationMessage?: string,
206
+ ): string {
207
+ this.semanticTokenRegistry.registerTokenType(id, description, superType, deprecationMessage);
208
+ if (scopesToProbe) {
209
+ this.doRegisterTokenStyleDefault(id, scopesToProbe);
210
+ }
211
+ return id;
212
+ }
213
+
214
+ registerMenus(menus: IMenuRegistry) {
215
+ menus.registerMenuItem(MenuId.SettingsIconMenu, {
216
+ command: THEME_TOGGLE_COMMAND.id,
217
+ group: '4_theme',
218
+ });
219
+ menus.registerMenuItem(MenuId.SettingsIconMenu, {
220
+ command: ICON_THEME_TOGGLE_COMMAND.id,
221
+ group: '4_theme',
222
+ });
223
+ }
224
+
225
+ registerCommands(commands: CommandRegistry) {
226
+ commands.registerCommand(THEME_TOGGLE_COMMAND, {
227
+ execute: async () => {
228
+ const themeInfos = this.themeService.getAvailableThemeInfos();
229
+ themeInfos.sort((a, b) => BuiltinThemeComparator[a.base] - BuiltinThemeComparator[b.base]);
230
+ let prevBase: BuiltinTheme;
231
+ const items = themeInfos.map((themeInfo) => {
232
+ if (prevBase !== themeInfo.base && !prevBase?.startsWith('hc')) {
233
+ prevBase = themeInfo.base;
234
+ return {
235
+ label: replaceLocalizePlaceholder(themeInfo.name)!,
236
+ value: themeInfo.themeId,
237
+ groupLabel: localize(getThemeTypeName(prevBase)),
238
+ };
239
+ }
240
+ return {
241
+ label: replaceLocalizePlaceholder(themeInfo.name)!,
242
+ value: themeInfo.themeId,
243
+ };
244
+ });
245
+ const defaultSelected = items.findIndex((opt) => opt.value === this.themeService.currentThemeId);
246
+ const prevThemeId = this.themeService.currentThemeId;
247
+ const themeId = await this.showPickWithPreview(
248
+ items,
249
+ {
250
+ selectIndex: () => defaultSelected,
251
+ placeholder: localize('theme.quickopen.plh'),
252
+ },
253
+ (value) => {
254
+ this.updateTopPreference('general.theme', value);
255
+ },
256
+ );
257
+
258
+ if (themeId && themeId === this.themeService.currentThemeId) {
259
+ return;
260
+ }
261
+ await this.updateTopPreference('general.theme', themeId || prevThemeId);
262
+ },
263
+ });
264
+ commands.registerCommand(ICON_THEME_TOGGLE_COMMAND, {
265
+ execute: async () => {
266
+ const themeInfos = this.iconService.getAvailableThemeInfos();
267
+ const items = themeInfos.map((themeInfo) => ({
268
+ label: themeInfo.name,
269
+ value: themeInfo.themeId,
270
+ }));
271
+ const defaultSelected = items.findIndex((opt) => opt.value === this.iconService.currentThemeId);
272
+ const prevThemeId = this.iconService.currentThemeId;
273
+ const themeId = await this.showPickWithPreview(
274
+ items,
275
+ {
276
+ selectIndex: () => defaultSelected,
277
+ placeholder: localize('icon.quickopen.plh'),
278
+ },
279
+ (value) => {
280
+ this.updateTopPreference(GeneralSettingsId.Icon, value);
281
+ },
282
+ );
283
+ if (themeId) {
284
+ await this.updateTopPreference(GeneralSettingsId.Icon, themeId);
285
+ } else {
286
+ await this.updateTopPreference(GeneralSettingsId.Icon, prevThemeId);
287
+ }
288
+ },
289
+ });
290
+ }
291
+
292
+ protected async updateTopPreference(key: string, value: string) {
293
+ const effectiveScope = this.preferenceService.resolve(key).scope;
294
+ // 最小就更新 User 的值
295
+ if (typeof effectiveScope === 'undefined' || effectiveScope <= PreferenceScope.User) {
296
+ await this.preferenceService.set(key, value, PreferenceScope.User);
297
+ } else {
298
+ await this.preferenceService.set(key, value, effectiveScope);
299
+ }
300
+ }
301
+
302
+ protected showPickWithPreview(
303
+ pickItems: { label: string; value: string; groupLabel?: string }[],
304
+ options: QuickOpenOptions,
305
+ onFocusChange: (value: string) => void,
306
+ ) {
307
+ return new Promise((resolve: (value: string | undefined) => void) => {
308
+ const items: QuickOpenItem[] = [];
309
+ pickItems.forEach((item, index) => {
310
+ const baseOption = {
311
+ label: item.label,
312
+ run: (mode: Mode) => {
313
+ if (mode === Mode.PREVIEW) {
314
+ onFocusChange(item.value);
315
+ return true;
316
+ }
317
+ if (mode === Mode.OPEN) {
318
+ resolve(item.value);
319
+ return true;
320
+ }
321
+ return false;
322
+ },
323
+ };
324
+ items.push(
325
+ new QuickOpenItem(
326
+ Object.assign(baseOption, { groupLabel: item.groupLabel, showBorder: !!item.groupLabel && index !== 0 }),
327
+ ),
328
+ );
329
+ });
330
+ this.quickOpenService.open(
331
+ {
332
+ onType: (_, acceptor) => acceptor(items),
333
+ },
334
+ {
335
+ onClose: () => resolve(undefined),
336
+ fuzzyMatchLabel: true,
337
+ showItemsWithoutHighlight: false,
338
+ ...options,
339
+ },
340
+ );
341
+ });
342
+ }
343
+ }