@theia/core 1.70.0-next.7 → 1.70.0-next.71

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 (194) hide show
  1. package/README.md +3 -3
  2. package/lib/browser/about-dialog.d.ts.map +1 -1
  3. package/lib/browser/about-dialog.js +3 -1
  4. package/lib/browser/about-dialog.js.map +1 -1
  5. package/lib/browser/catalog.json +83 -7
  6. package/lib/browser/common-frontend-contribution.d.ts +0 -2
  7. package/lib/browser/common-frontend-contribution.d.ts.map +1 -1
  8. package/lib/browser/common-frontend-contribution.js +39 -15
  9. package/lib/browser/common-frontend-contribution.js.map +1 -1
  10. package/lib/browser/frontend-application-module.d.ts.map +1 -1
  11. package/lib/browser/frontend-application-module.js +1 -0
  12. package/lib/browser/frontend-application-module.js.map +1 -1
  13. package/lib/browser/hover-service.d.ts.map +1 -1
  14. package/lib/browser/hover-service.js +5 -3
  15. package/lib/browser/hover-service.js.map +1 -1
  16. package/lib/browser/markdown-rendering/markdown-renderer.d.ts +1 -1
  17. package/lib/browser/markdown-rendering/markdown-renderer.d.ts.map +1 -1
  18. package/lib/browser/menu/browser-menu-plugin.d.ts.map +1 -1
  19. package/lib/browser/menu/browser-menu-plugin.js +9 -1
  20. package/lib/browser/menu/browser-menu-plugin.js.map +1 -1
  21. package/lib/browser/quick-input/quick-input-service.spec.js +53 -9
  22. package/lib/browser/quick-input/quick-input-service.spec.js.map +1 -1
  23. package/lib/browser/saveable-service.d.ts +24 -1
  24. package/lib/browser/saveable-service.d.ts.map +1 -1
  25. package/lib/browser/saveable-service.js +34 -3
  26. package/lib/browser/saveable-service.js.map +1 -1
  27. package/lib/browser/tree/fuzzy-search.d.ts +1 -60
  28. package/lib/browser/tree/fuzzy-search.d.ts.map +1 -1
  29. package/lib/browser/tree/fuzzy-search.js +3 -58
  30. package/lib/browser/tree/fuzzy-search.js.map +1 -1
  31. package/lib/browser/tree/tree-view-welcome-widget.d.ts.map +1 -1
  32. package/lib/browser/tree/tree-view-welcome-widget.js +1 -2
  33. package/lib/browser/tree/tree-view-welcome-widget.js.map +1 -1
  34. package/lib/browser/widgets/select-component.d.ts +1 -0
  35. package/lib/browser/widgets/select-component.d.ts.map +1 -1
  36. package/lib/browser/widgets/select-component.js +30 -0
  37. package/lib/browser/widgets/select-component.js.map +1 -1
  38. package/lib/browser/window/default-window-service.d.ts +2 -1
  39. package/lib/browser/window/default-window-service.d.ts.map +1 -1
  40. package/lib/browser/window/default-window-service.js +5 -1
  41. package/lib/browser/window/default-window-service.js.map +1 -1
  42. package/lib/browser/window/test/mock-window-service.d.ts +2 -1
  43. package/lib/browser/window/test/mock-window-service.d.ts.map +1 -1
  44. package/lib/browser/window/test/mock-window-service.js +2 -1
  45. package/lib/browser/window/test/mock-window-service.js.map +1 -1
  46. package/lib/browser/window/window-service.d.ts +8 -1
  47. package/lib/browser/window/window-service.d.ts.map +1 -1
  48. package/lib/common/fuzzy-match-utils.d.ts +27 -0
  49. package/lib/common/fuzzy-match-utils.d.ts.map +1 -0
  50. package/lib/common/fuzzy-match-utils.js +96 -0
  51. package/lib/common/fuzzy-match-utils.js.map +1 -0
  52. package/lib/common/fuzzy-match-utils.spec.d.ts +2 -0
  53. package/lib/common/fuzzy-match-utils.spec.d.ts.map +1 -0
  54. package/lib/common/fuzzy-match-utils.spec.js +109 -0
  55. package/lib/common/fuzzy-match-utils.spec.js.map +1 -0
  56. package/lib/common/fuzzy-search.d.ts +63 -0
  57. package/lib/common/fuzzy-search.d.ts.map +1 -0
  58. package/lib/common/fuzzy-search.js +101 -0
  59. package/lib/common/fuzzy-search.js.map +1 -0
  60. package/lib/common/fuzzy-search.spec.d.ts.map +1 -0
  61. package/lib/{browser/tree → common}/fuzzy-search.spec.js +20 -1
  62. package/lib/common/fuzzy-search.spec.js.map +1 -0
  63. package/lib/common/logger-sanitizer.d.ts +9 -0
  64. package/lib/common/logger-sanitizer.d.ts.map +1 -1
  65. package/lib/common/logger-sanitizer.js +26 -3
  66. package/lib/common/logger-sanitizer.js.map +1 -1
  67. package/lib/common/logger-sanitizer.spec.js +41 -0
  68. package/lib/common/logger-sanitizer.spec.js.map +1 -1
  69. package/lib/common/preferences/injectable-preference-proxy.d.ts +5 -3
  70. package/lib/common/preferences/injectable-preference-proxy.d.ts.map +1 -1
  71. package/lib/common/preferences/injectable-preference-proxy.js +9 -6
  72. package/lib/common/preferences/injectable-preference-proxy.js.map +1 -1
  73. package/lib/common/preferences/preference-configurations.d.ts +2 -2
  74. package/lib/common/preferences/preference-configurations.d.ts.map +1 -1
  75. package/lib/common/preferences/preference-configurations.js +1 -1
  76. package/lib/common/preferences/preference-configurations.js.map +1 -1
  77. package/lib/common/preferences/preference-provider-impl.d.ts +4 -3
  78. package/lib/common/preferences/preference-provider-impl.d.ts.map +1 -1
  79. package/lib/common/preferences/preference-provider-impl.js +9 -6
  80. package/lib/common/preferences/preference-provider-impl.js.map +1 -1
  81. package/lib/common/preferences/preference-proxy.d.ts +4 -2
  82. package/lib/common/preferences/preference-proxy.d.ts.map +1 -1
  83. package/lib/common/preferences/preference-proxy.js +4 -5
  84. package/lib/common/preferences/preference-proxy.js.map +1 -1
  85. package/lib/common/preferences/preference-service.d.ts +4 -3
  86. package/lib/common/preferences/preference-service.d.ts.map +1 -1
  87. package/lib/common/preferences/preference-service.js +13 -10
  88. package/lib/common/preferences/preference-service.js.map +1 -1
  89. package/lib/common/quick-pick-service.d.ts +18 -2
  90. package/lib/common/quick-pick-service.d.ts.map +1 -1
  91. package/lib/common/quick-pick-service.js +53 -8
  92. package/lib/common/quick-pick-service.js.map +1 -1
  93. package/lib/common/uri.js +1 -1
  94. package/lib/common/uri.js.map +1 -1
  95. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
  96. package/lib/electron-browser/menu/electron-main-menu-factory.js +4 -6
  97. package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
  98. package/lib/electron-browser/menu/electron-menu-contribution.js +3 -3
  99. package/lib/electron-browser/menu/electron-menu-contribution.js.map +1 -1
  100. package/lib/electron-browser/messaging/electron-local-ws-connection-source.d.ts +2 -0
  101. package/lib/electron-browser/messaging/electron-local-ws-connection-source.d.ts.map +1 -1
  102. package/lib/electron-browser/messaging/electron-local-ws-connection-source.js +5 -3
  103. package/lib/electron-browser/messaging/electron-local-ws-connection-source.js.map +1 -1
  104. package/lib/electron-browser/preload.js +2 -2
  105. package/lib/electron-browser/preload.js.map +1 -1
  106. package/lib/electron-browser/window/electron-secondary-window-service.d.ts +4 -0
  107. package/lib/electron-browser/window/electron-secondary-window-service.d.ts.map +1 -1
  108. package/lib/electron-browser/window/electron-secondary-window-service.js +32 -0
  109. package/lib/electron-browser/window/electron-secondary-window-service.js.map +1 -1
  110. package/lib/electron-browser/window/electron-window-module.d.ts +1 -0
  111. package/lib/electron-browser/window/electron-window-module.d.ts.map +1 -1
  112. package/lib/electron-browser/window/electron-window-module.js +4 -0
  113. package/lib/electron-browser/window/electron-window-module.js.map +1 -1
  114. package/lib/electron-browser/window/electron-window-service.d.ts +3 -2
  115. package/lib/electron-browser/window/electron-window-service.d.ts.map +1 -1
  116. package/lib/electron-browser/window/electron-window-service.js +7 -4
  117. package/lib/electron-browser/window/electron-window-service.js.map +1 -1
  118. package/lib/electron-browser/window/window-zoom-action-bar.d.ts +21 -0
  119. package/lib/electron-browser/window/window-zoom-action-bar.d.ts.map +1 -0
  120. package/lib/electron-browser/window/window-zoom-action-bar.js +71 -0
  121. package/lib/electron-browser/window/window-zoom-action-bar.js.map +1 -0
  122. package/lib/electron-browser/window/window-zoom-status-bar-item.d.ts +14 -0
  123. package/lib/electron-browser/window/window-zoom-status-bar-item.d.ts.map +1 -0
  124. package/lib/electron-browser/window/window-zoom-status-bar-item.js +87 -0
  125. package/lib/electron-browser/window/window-zoom-status-bar-item.js.map +1 -0
  126. package/lib/electron-common/electron-api.d.ts +1 -1
  127. package/lib/electron-common/electron-api.d.ts.map +1 -1
  128. package/lib/electron-common/electron-main-window-service.d.ts +2 -1
  129. package/lib/electron-common/electron-main-window-service.d.ts.map +1 -1
  130. package/lib/electron-common/electron-window-preferences.d.ts +5 -2
  131. package/lib/electron-common/electron-window-preferences.d.ts.map +1 -1
  132. package/lib/electron-common/electron-window-preferences.js +16 -10
  133. package/lib/electron-common/electron-window-preferences.js.map +1 -1
  134. package/lib/electron-main/electron-api-main.d.ts.map +1 -1
  135. package/lib/electron-main/electron-api-main.js +14 -2
  136. package/lib/electron-main/electron-api-main.js.map +1 -1
  137. package/lib/electron-main/electron-main-application.d.ts +1 -0
  138. package/lib/electron-main/electron-main-application.d.ts.map +1 -1
  139. package/lib/electron-main/electron-main-application.js +6 -0
  140. package/lib/electron-main/electron-main-application.js.map +1 -1
  141. package/lib/electron-main/electron-main-window-service-impl.d.ts +2 -1
  142. package/lib/electron-main/electron-main-window-service-impl.d.ts.map +1 -1
  143. package/lib/electron-main/electron-main-window-service-impl.js +6 -2
  144. package/lib/electron-main/electron-main-window-service-impl.js.map +1 -1
  145. package/package.json +7 -7
  146. package/src/browser/about-dialog.tsx +3 -1
  147. package/src/browser/common-frontend-contribution.ts +36 -17
  148. package/src/browser/frontend-application-module.ts +2 -1
  149. package/src/browser/hover-service.ts +5 -3
  150. package/src/browser/markdown-rendering/markdown-renderer.ts +1 -1
  151. package/src/browser/menu/browser-menu-plugin.ts +9 -1
  152. package/src/browser/quick-input/quick-input-service.spec.ts +58 -9
  153. package/src/browser/saveable-service.ts +56 -4
  154. package/src/browser/style/hover-service.css +1 -0
  155. package/src/browser/style/index.css +1 -1
  156. package/src/browser/tree/fuzzy-search.ts +2 -120
  157. package/src/browser/tree/tree-view-welcome-widget.tsx +1 -2
  158. package/src/browser/widgets/select-component.tsx +39 -2
  159. package/src/browser/window/default-window-service.ts +6 -1
  160. package/src/browser/window/test/mock-window-service.ts +2 -1
  161. package/src/browser/window/window-service.ts +9 -1
  162. package/src/common/fuzzy-match-utils.spec.ts +141 -0
  163. package/src/common/fuzzy-match-utils.ts +78 -0
  164. package/src/{browser/tree → common}/fuzzy-search.spec.ts +23 -1
  165. package/src/common/fuzzy-search.ts +158 -0
  166. package/src/common/i18n/nls.metadata.json +25379 -24577
  167. package/src/common/logger-sanitizer.spec.ts +54 -1
  168. package/src/common/logger-sanitizer.ts +38 -3
  169. package/src/common/preferences/injectable-preference-proxy.ts +6 -3
  170. package/src/common/preferences/preference-configurations.ts +2 -2
  171. package/src/common/preferences/preference-provider-impl.ts +6 -3
  172. package/src/common/preferences/preference-proxy.ts +6 -4
  173. package/src/common/preferences/preference-service.ts +6 -3
  174. package/src/common/quick-pick-service.ts +65 -11
  175. package/src/common/uri.ts +1 -1
  176. package/src/electron-browser/menu/electron-main-menu-factory.ts +4 -6
  177. package/src/electron-browser/menu/electron-menu-contribution.ts +4 -4
  178. package/src/electron-browser/messaging/electron-local-ws-connection-source.ts +4 -2
  179. package/src/electron-browser/preload.ts +2 -2
  180. package/src/electron-browser/style/window-zoom-action-bar.css +57 -0
  181. package/src/electron-browser/window/electron-secondary-window-service.ts +32 -1
  182. package/src/electron-browser/window/electron-window-module.ts +4 -0
  183. package/src/electron-browser/window/electron-window-service.ts +10 -7
  184. package/src/electron-browser/window/window-zoom-action-bar.tsx +115 -0
  185. package/src/electron-browser/window/window-zoom-status-bar-item.ts +81 -0
  186. package/src/electron-common/electron-api.ts +1 -1
  187. package/src/electron-common/electron-main-window-service.ts +2 -1
  188. package/src/electron-common/electron-window-preferences.ts +19 -11
  189. package/src/electron-main/electron-api-main.ts +12 -2
  190. package/src/electron-main/electron-main-application.ts +7 -0
  191. package/src/electron-main/electron-main-window-service-impl.ts +7 -2
  192. package/lib/browser/tree/fuzzy-search.spec.d.ts.map +0 -1
  193. package/lib/browser/tree/fuzzy-search.spec.js.map +0 -1
  194. /package/lib/{browser/tree → common}/fuzzy-search.spec.d.ts +0 -0
@@ -15,7 +15,7 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { expect } from 'chai';
18
- import { DefaultLoggerSanitizer } from './logger-sanitizer';
18
+ import { DefaultLoggerSanitizer, DefaultSanitizationRules } from './logger-sanitizer';
19
19
 
20
20
  describe('DefaultLoggerSanitizer', () => {
21
21
  let sanitizer: DefaultLoggerSanitizer;
@@ -285,4 +285,57 @@ describe('DefaultLoggerSanitizer', () => {
285
285
  expect(sanitized).to.equal('"serverAuthToken": "****"');
286
286
  });
287
287
  });
288
+
289
+ describe('precheck optimization', () => {
290
+ describe('URL precheck', () => {
291
+ it('should return false when :// is missing', () => {
292
+ const urlRule = DefaultSanitizationRules[0];
293
+
294
+ expect(urlRule.precheck!('user@host')).to.be.false;
295
+ expect(urlRule.precheck!('email@example.com')).to.be.false;
296
+ expect(urlRule.precheck!('no url here')).to.be.false;
297
+ });
298
+
299
+ it('should return false when @ is missing even if :// exists', () => {
300
+ const urlRule = DefaultSanitizationRules[0];
301
+
302
+ expect(urlRule.precheck!('https://example.com/path')).to.be.false;
303
+ expect(urlRule.precheck!('http://localhost:8080')).to.be.false;
304
+ });
305
+
306
+ it('should return true only when both :// and @ exist', () => {
307
+ const urlRule = DefaultSanitizationRules[0];
308
+
309
+ expect(urlRule.precheck!('http://user:pass@host.com')).to.be.true;
310
+ expect(urlRule.precheck!('https://u:p@example.com')).to.be.true;
311
+ });
312
+ });
313
+
314
+ describe('API key precheck', () => {
315
+ it('should return false when patterns are missing', () => {
316
+ const apiKeyRule = DefaultSanitizationRules[1];
317
+
318
+ expect(apiKeyRule.precheck!('normal log message')).to.be.false;
319
+ expect(apiKeyRule.precheck!('just api without the other word')).to.be.false;
320
+ expect(apiKeyRule.precheck!('just key without the other word')).to.be.false;
321
+ expect(apiKeyRule.precheck!('login failed')).to.be.false;
322
+ });
323
+
324
+ it('should return true when api and key patterns exist', () => {
325
+ const apiKeyRule = DefaultSanitizationRules[1];
326
+
327
+ expect(apiKeyRule.precheck!('"api_key": "value"')).to.be.true;
328
+ expect(apiKeyRule.precheck!('API_KEY=something')).to.be.true;
329
+ expect(apiKeyRule.precheck!('the apiKey is set')).to.be.true;
330
+ });
331
+
332
+ it('should return true when auth and token patterns exist', () => {
333
+ const apiKeyRule = DefaultSanitizationRules[1];
334
+
335
+ expect(apiKeyRule.precheck!('"auth_token": "value"')).to.be.true;
336
+ expect(apiKeyRule.precheck!('AUTH_TOKEN=something')).to.be.true;
337
+ expect(apiKeyRule.precheck!('the authToken is set')).to.be.true;
338
+ });
339
+ });
340
+ });
288
341
  });
@@ -65,6 +65,34 @@ export interface SanitizationRule {
65
65
  * The replacement string. Can include capture group references like $1, $2, etc.
66
66
  */
67
67
  replacement: string;
68
+
69
+ /**
70
+ * Optional quick check function that returns true if the message might contain
71
+ * sensitive data matching this rule. Used as a fast early-exit optimization
72
+ * to avoid running expensive regex operations on messages that definitely
73
+ * don't contain sensitive data.
74
+ *
75
+ * If not provided, the regex pattern will always be executed.
76
+ */
77
+ precheck?: (message: string) => boolean;
78
+ }
79
+
80
+ /**
81
+ * Checks if message might contain a URL with credentials.
82
+ * Checks for :// (required for any URL) and @ (required for credentials).
83
+ */
84
+ function mightContainUrlCredentials(message: string): boolean {
85
+ return message.includes('://') && message.includes('@');
86
+ }
87
+
88
+ /**
89
+ * Checks if message might contain API key or auth token patterns.
90
+ * Uses lowercase comparison for case-insensitive matching.
91
+ */
92
+ function mightContainApiKeyOrToken(message: string): boolean {
93
+ const lower = message.toLowerCase();
94
+ return (lower.includes('api') && lower.includes('key')) ||
95
+ (lower.includes('auth') && lower.includes('token'));
68
96
  }
69
97
 
70
98
  /**
@@ -78,7 +106,8 @@ export const DefaultSanitizationRules: SanitizationRule[] = [
78
106
  * Capture groups: $1=protocol, $2=username, $3=password, $4=host (with optional port)
79
107
  */
80
108
  pattern: /([a-z][a-z0-9+.-]*:\/\/)([^:/@]+):([^:/@]+)@([^/:@\s]+(?::\d+)?)/giu,
81
- replacement: '$1****:****@$4'
109
+ replacement: '$1****:****@$4',
110
+ precheck: mightContainUrlCredentials
82
111
  },
83
112
  {
84
113
  /**
@@ -88,7 +117,8 @@ export const DefaultSanitizationRules: SanitizationRule[] = [
88
117
  * Capture groups: $1=key with opening quote of value, $2=closing quote of value
89
118
  */
90
119
  pattern: /(\\?["'][\w.-]*(?:api[_-]?key|auth[_-]?token)\\?["']\s*:\s*\\?["'])[^"'\\]+(\\?["'])/gi,
91
- replacement: '$1****$2'
120
+ replacement: '$1****$2',
121
+ precheck: mightContainApiKeyOrToken
92
122
  }
93
123
  ];
94
124
 
@@ -101,9 +131,14 @@ export class DefaultLoggerSanitizer implements LoggerSanitizer {
101
131
  protected rules: SanitizationRule[] = DefaultSanitizationRules;
102
132
 
103
133
  sanitize(message: string): string {
134
+ if (!message) {
135
+ return message;
136
+ }
104
137
  let result = message;
105
138
  for (const rule of this.rules) {
106
- result = result.replace(rule.pattern, rule.replacement);
139
+ if (!rule.precheck || rule.precheck(result)) {
140
+ result = result.replace(rule.pattern, rule.replacement);
141
+ }
107
142
  }
108
143
  return result;
109
144
  }
@@ -15,13 +15,16 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { inject, injectable, postConstruct } from 'inversify';
18
- import { PreferenceSchema } from '../../common/preferences/preference-schema';
19
- import { Disposable, DisposableCollection, Emitter, Event, isObject, MaybePromise, PreferenceScope } from '../../common';
18
+ import { PreferenceSchema } from './preference-schema';
20
19
  import { PreferenceChangeEvent, PreferenceEventEmitter, PreferenceProxy, PreferenceProxyOptions, PreferenceRetrieval } from './preference-proxy';
21
20
  import { PreferenceChange, PreferenceChangeImpl, PreferenceChanges, PreferenceService } from './preference-service';
22
21
  import { JSONValue } from '@lumino/coreutils';
23
22
  import { PreferenceProviderDataChange } from './preference-provider';
24
- import { OverridePreferenceName } from '../../common/preferences/preference-language-override-service';
23
+ import { OverridePreferenceName } from './preference-language-override-service';
24
+ import { isObject, MaybePromise } from '../types';
25
+ import { DisposableCollection } from '../disposable';
26
+ import { Emitter, Event } from '../event';
27
+ import { PreferenceScope } from './preference-scope';
25
28
 
26
29
  export const PreferenceProxySchema = Symbol('PreferenceProxySchema');
27
30
  export interface PreferenceProxyFactory {
@@ -15,8 +15,8 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { injectable, inject, named, interfaces } from 'inversify';
18
- import URI from '../../common/uri';
19
- import { ContributionProvider, bindRootContributionProvider } from '../../common/contribution-provider';
18
+ import URI from '../uri';
19
+ import { ContributionProvider, bindRootContributionProvider } from '../contribution-provider';
20
20
 
21
21
  export const PreferenceConfiguration = Symbol('PreferenceConfiguration');
22
22
  export interface PreferenceConfiguration {
@@ -17,11 +17,14 @@
17
17
  import debounce = require('p-debounce');
18
18
  import { injectable } from 'inversify';
19
19
  import { JSONObject } from '@lumino/coreutils';
20
- import URI from '../../common/uri';
21
- import { DisposableCollection, Emitter, Event, isObject, PreferenceLanguageOverrideService } from '../../common';
22
- import { Deferred } from '../../common/promise-util';
20
+ import URI from '../uri';
21
+ import { Emitter, Event } from '../event';
22
+ import { Deferred } from '../promise-util';
23
23
  import { PreferenceScope } from './preference-scope';
24
24
  import { PreferenceProvider, PreferenceProviderDataChange, PreferenceProviderDataChanges, PreferenceResolveResult } from './preference-provider';
25
+ import { DisposableCollection } from '../disposable';
26
+ import { PreferenceLanguageOverrideService } from './preference-language-override-service';
27
+ import { isObject } from '../types';
25
28
 
26
29
  export abstract class PreferenceProviderBase {
27
30
 
@@ -16,12 +16,14 @@
16
16
 
17
17
  /* eslint-disable @typescript-eslint/no-explicit-any */
18
18
 
19
- import { Disposable, Event, isObject, MaybePromise } from '../../common';
20
19
  import { PreferenceService } from './preference-service';
21
20
  import { PreferenceScope } from './preference-scope';
22
- import { IJSONSchema } from '../../common/json-schema';
23
- import { isThenable } from '../../common/promise-util';
24
- import { OverridePreferenceName } from '../../common/preferences/preference-language-override-service';
21
+ import { IJSONSchema } from '../json-schema';
22
+ import { isThenable } from '../promise-util';
23
+ import { OverridePreferenceName } from './preference-language-override-service';
24
+ import { isObject, MaybePromise } from '../types';
25
+ import { Event } from '../event';
26
+ import { Disposable } from '../disposable';
25
27
 
26
28
  /**
27
29
  * It is worth explaining the type for `PreferenceChangeEvent`:
@@ -18,14 +18,17 @@
18
18
 
19
19
  import { JSONValue } from '@lumino/coreutils';
20
20
  import { inject, injectable, postConstruct } from 'inversify';
21
- import { Disposable, DisposableCollection, Emitter, Event, deepFreeze, unreachable } from '../../common';
22
- import { Deferred } from '../../common/promise-util';
23
- import URI from '../../common/uri';
21
+ import { Disposable, DisposableCollection } from '../disposable';
22
+ import { Emitter, Event } from '../event';
23
+ import { Deferred } from '../promise-util';
24
+ import URI from '../uri';
24
25
  import { OverridePreferenceName, PreferenceLanguageOverrideService } from './preference-language-override-service';
25
26
  import { PreferenceProvider, PreferenceProviderDataChange, PreferenceProviderDataChanges, PreferenceResolveResult, PreferenceUtils } from './preference-provider';
26
27
  import { PreferenceSchemaService } from './preference-schema';
27
28
  import { PreferenceScope } from './preference-scope';
28
29
  import { PreferenceConfigurations } from './preference-configurations';
30
+ import { deepFreeze } from '../objects';
31
+ import { unreachable } from '../types';
29
32
 
30
33
  /**
31
34
  * Representation of a preference change. A preference value can be set to `undefined` for a specific scope.
@@ -19,6 +19,7 @@ import { Event } from './event';
19
19
  import { KeySequence } from './keys';
20
20
  import { CancellationToken } from './cancellation';
21
21
  import { Severity } from './severity';
22
+ import { findSubstringIndex, matchRank } from './fuzzy-match-utils';
22
23
 
23
24
  export const quickPickServicePath = '/services/quickPick';
24
25
  export const QuickPickService = Symbol('QuickPickService');
@@ -92,6 +93,26 @@ export interface QuickPickValue<V> extends QuickPickItem {
92
93
  value: V
93
94
  }
94
95
 
96
+ /**
97
+ * Specifies the location where a {@link QuickInputButton} should be rendered.
98
+ */
99
+ export enum QuickInputButtonLocation {
100
+ /**
101
+ * The button is rendered in the title bar.
102
+ */
103
+ Title = 1,
104
+
105
+ /**
106
+ * The button is rendered inline to the right of the input box.
107
+ */
108
+ Inline = 2,
109
+
110
+ /**
111
+ * The button is rendered at the far end inside the input box.
112
+ */
113
+ Input = 3
114
+ }
115
+
95
116
  export interface QuickInputButton {
96
117
  iconClass?: string;
97
118
  tooltip?: string;
@@ -101,9 +122,8 @@ export interface QuickInputButton {
101
122
  alwaysVisible?: boolean;
102
123
  /**
103
124
  * The location where the button should be rendered.
104
- * @monaco-uplift: consider using a typed enum matching Monaco's QuickInputButtonLocation instead of number.
105
125
  */
106
- location?: number;
126
+ location?: QuickInputButtonLocation;
107
127
  /**
108
128
  * When present, indicates that the button is a toggle button.
109
129
  */
@@ -303,24 +323,52 @@ export function filterItems(items: QuickPickItemOrSeparator[], filter: string):
303
323
  return items;
304
324
  }
305
325
 
306
- const filteredItems: QuickPickItemOrSeparator[] = [];
326
+ function matchesFilter(item: QuickPickItem): boolean {
327
+ return fuzzy.test(filter, item.label) ||
328
+ (!!item.description && fuzzy.test(filter, item.description)) ||
329
+ (!!item.detail && fuzzy.test(filter, item.detail));
330
+ }
331
+
332
+ function itemMatchRank(item: QuickPickItem): number {
333
+ return Math.min(
334
+ matchRank(item.label, filter),
335
+ item.description ? matchRank(item.description, filter) : 2,
336
+ item.detail ? matchRank(item.detail, filter) : 2
337
+ );
338
+ }
339
+
340
+ // Process items in separator groups, sorted by match rank within each group.
341
+ const result: QuickPickItemOrSeparator[] = [];
342
+ let currentSeparator: QuickPickSeparator | undefined;
343
+ let groupMatches: { item: QuickPickItem; rank: number }[] = [];
344
+
345
+ const flushGroup = (): void => {
346
+ if (groupMatches.length > 0) {
347
+ if (currentSeparator) {
348
+ result.push(currentSeparator);
349
+ }
350
+ groupMatches.sort((a, b) => a.rank - b.rank);
351
+ result.push(...groupMatches.map(m => m.item));
352
+ }
353
+ groupMatches = [];
354
+ };
355
+
307
356
  for (const item of items) {
308
357
  if (item.type === 'separator') {
309
- filteredItems.push(item);
310
- } else if (
311
- fuzzy.test(filter, item.label) ||
312
- (item.description && fuzzy.test(filter, item.description)) ||
313
- (item.detail && fuzzy.test(filter, item.detail))
314
- ) {
358
+ flushGroup();
359
+ currentSeparator = item;
360
+ } else if (matchesFilter(item)) {
315
361
  item.highlights = {
316
362
  label: findMatches(item.label, filter),
317
363
  description: item.description ? findMatches(item.description, filter) : undefined,
318
364
  detail: item.detail ? findMatches(item.detail, filter) : undefined
319
365
  };
320
- filteredItems.push(item);
366
+ groupMatches.push({ item, rank: itemMatchRank(item) });
321
367
  }
322
368
  }
323
- return filteredItems;
369
+ flushGroup();
370
+
371
+ return result;
324
372
  }
325
373
 
326
374
  /**
@@ -337,6 +385,12 @@ export function findMatches(word: string, pattern: string): Array<{ start: numbe
337
385
  return undefined;
338
386
  }
339
387
 
388
+ // Prefer a contiguous substring highlight over scattered fuzzy character highlights.
389
+ const substringIndex = findSubstringIndex(word, pattern);
390
+ if (substringIndex !== -1) {
391
+ return [{ start: substringIndex, end: substringIndex + pattern.length }];
392
+ }
393
+
340
394
  const delimiter = '\u0000'; // null byte that shouldn't appear in the input and is used to denote matches.
341
395
  const matchResult = fuzzy.match(pattern.replace(/\u0000/gu, ''), word, { pre: delimiter, post: delimiter });
342
396
  if (!matchResult) {
package/src/common/uri.ts CHANGED
@@ -22,7 +22,7 @@ export class URI {
22
22
  public static fromComponents(components: UriComponents): URI;
23
23
  public static fromComponents(components: undefined): undefined;
24
24
  public static fromComponents(components: UriComponents | undefined): URI | undefined {
25
- return components ? new URI(Uri.revive(components)) : undefined;
25
+ return components ? new URI(Uri.from(components)) : undefined;
26
26
  }
27
27
 
28
28
  public static fromFilePath(path: string): URI {
@@ -233,12 +233,10 @@ export class ElectronMainMenuFactory extends BrowserMainMenuFactory {
233
233
  }
234
234
  };
235
235
 
236
- if (isOSX) {
237
- const role = this.roleFor(menu.id);
238
- if (role) {
239
- menuItem.role = role;
240
- delete menuItem.execute;
241
- }
236
+ const role = this.roleFor(menu.id);
237
+ if (role) {
238
+ menuItem.role = role;
239
+ delete menuItem.execute;
242
240
  }
243
241
  parentItems.push(menuItem);
244
242
  }
@@ -26,7 +26,7 @@ import {
26
26
  import { ElectronMainMenuFactory } from './electron-main-menu-factory';
27
27
  import { FrontendApplicationStateService, FrontendApplicationState } from '../../browser/frontend-application-state';
28
28
  import { FrontendApplicationConfigProvider } from '../../browser/frontend-application-config-provider';
29
- import { ZoomLevel } from '../../electron-common/electron-window-preferences';
29
+ import { PREF_WINDOW_ZOOM_LEVEL, ZoomLevel } from '../../electron-common/electron-window-preferences';
30
30
  import { BrowserMenuBarContribution } from '../../browser/menu/browser-menu-plugin';
31
31
  import { WindowService } from '../../browser/window/window-service';
32
32
  import { WindowTitleService } from '../../browser/window/window-title-service';
@@ -316,7 +316,7 @@ export class ElectronMenuContribution extends BrowserMenuBarContribution impleme
316
316
  zoomLevel = ZoomLevel.MAX;
317
317
  return;
318
318
  };
319
- this.preferenceService.set('window.zoomLevel', zoomLevel, PreferenceScope.User);
319
+ this.preferenceService.set(PREF_WINDOW_ZOOM_LEVEL, zoomLevel, PreferenceScope.User);
320
320
  }
321
321
  });
322
322
  registry.registerCommand(ElectronCommands.ZOOM_OUT, {
@@ -328,11 +328,11 @@ export class ElectronMenuContribution extends BrowserMenuBarContribution impleme
328
328
  zoomLevel = ZoomLevel.MIN;
329
329
  return;
330
330
  };
331
- this.preferenceService.set('window.zoomLevel', zoomLevel, PreferenceScope.User);
331
+ this.preferenceService.set(PREF_WINDOW_ZOOM_LEVEL, zoomLevel, PreferenceScope.User);
332
332
  }
333
333
  });
334
334
  registry.registerCommand(ElectronCommands.RESET_ZOOM, {
335
- execute: () => this.preferenceService.set('window.zoomLevel', ZoomLevel.DEFAULT, PreferenceScope.User)
335
+ execute: () => this.preferenceService.set(PREF_WINDOW_ZOOM_LEVEL, ZoomLevel.DEFAULT, PreferenceScope.User)
336
336
  });
337
337
 
338
338
  registry.registerCommand(ElectronCommands.TOGGLE_FULL_SCREEN, {
@@ -18,14 +18,16 @@ import { injectable } from 'inversify';
18
18
  import { Endpoint } from '../../browser/endpoint';
19
19
  import { WebSocketConnectionSource } from '../../browser/messaging/ws-connection-source';
20
20
 
21
+ export const LOCAL_PORT_PARAM = 'localPort';
21
22
  export function getLocalPort(): string | undefined {
22
23
  const params = new URLSearchParams(location.search);
23
- return params.get('localPort') ?? undefined;
24
+ return params.get(LOCAL_PORT_PARAM) ?? undefined;
24
25
  }
25
26
 
27
+ export const CURRENT_PORT_PARAM = 'port';
26
28
  export function getCurrentPort(): string | undefined {
27
29
  const params = new URLSearchParams(location.search);
28
- return params.get('port') ?? undefined;
30
+ return params.get(CURRENT_PORT_PARAM) ?? undefined;
29
31
  }
30
32
 
31
33
  @injectable()
@@ -207,8 +207,8 @@ const api: TheiaCoreAPI = {
207
207
  return ipcRenderer.invoke(CHANNEL_GET_ZOOM_LEVEL);
208
208
  },
209
209
 
210
- setZoomLevel: function (desired: number): void {
211
- ipcRenderer.send(CHANNEL_SET_ZOOM_LEVEL, desired);
210
+ setZoomLevel: function (desired: number, windowName?: string): void {
211
+ ipcRenderer.send(CHANNEL_SET_ZOOM_LEVEL, desired, windowName);
212
212
  },
213
213
  isFullScreenable: function (): boolean {
214
214
  return ipcRenderer.sendSync(CHANNEL_IS_FULL_SCREENABLE);
@@ -0,0 +1,57 @@
1
+ /********************************************************************************
2
+ * Copyright (C) 2026 EclipseSource and others.
3
+ *
4
+ * This program and the accompanying materials are made available under the
5
+ * terms of the Eclipse Public License v. 2.0 which is available at
6
+ * http://www.eclipse.org/legal/epl-2.0.
7
+ *
8
+ * This Source Code may also be made available under the following Secondary
9
+ * Licenses when the conditions for such availability set forth in the Eclipse
10
+ * Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ * with the GNU Classpath Exception which is available at
12
+ * https://www.gnu.org/software/classpath/license.html.
13
+ *
14
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ ********************************************************************************/
16
+
17
+ .window-zoom-action-bar {
18
+ display: flex;
19
+ align-items: center;
20
+ gap: calc(var(--theia-ui-padding) / 3);
21
+ line-height: 1;
22
+ }
23
+
24
+ .window-zoom-action-bar .codicon {
25
+ font-size: inherit;
26
+ }
27
+
28
+ .window-zoom-display {
29
+ display: flex;
30
+ align-items: center;
31
+ justify-content: center;
32
+ min-width: 1.5em;
33
+ padding: calc(var(--theia-ui-padding) / 3);
34
+ cursor: default;
35
+ user-select: none;
36
+ }
37
+
38
+ .window-zoom-button {
39
+ display: flex;
40
+ align-items: center;
41
+ justify-content: center;
42
+ padding: calc(var(--theia-ui-padding) / 3);
43
+ border-radius: calc(var(--theia-ui-padding) / 2);
44
+ white-space: nowrap;
45
+ }
46
+
47
+ .window-zoom-button:hover {
48
+ background-color: var(--theia-toolbar-hoverBackground);
49
+ }
50
+
51
+ .window-zoom-button:focus-visible {
52
+ outline: 1px solid var(--theia-focusBorder);
53
+ }
54
+
55
+ #status-bar-window-zoom-status {
56
+ padding-inline: calc(var(--theia-ui-padding) * 2 / 3);
57
+ }
@@ -14,14 +14,41 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { injectable } from 'inversify';
17
+ import { inject, injectable, postConstruct } from 'inversify';
18
18
  import { DefaultSecondaryWindowService } from '../../browser/window/default-secondary-window-service';
19
19
  import { ApplicationShell, ExtractableWidget } from '../../browser';
20
20
  import { ElectronWindowService } from './electron-window-service';
21
21
  import { Deferred, timeout } from '../../common/promise-util';
22
+ import { ElectronWindowPreferences, PREF_WINDOW_ZOOM_LEVEL } from '../../electron-common/electron-window-preferences';
22
23
 
23
24
  @injectable()
24
25
  export class ElectronSecondaryWindowService extends DefaultSecondaryWindowService {
26
+
27
+ @inject(ElectronWindowPreferences)
28
+ protected readonly electronWindowPreferences: ElectronWindowPreferences;
29
+
30
+ @postConstruct()
31
+ override init(): void {
32
+ super.init();
33
+ this.electronWindowPreferences.ready.then(() => {
34
+ const initialZoomLevel = this.electronWindowPreferences.get(PREF_WINDOW_ZOOM_LEVEL);
35
+ this.updateWindowZoomLevel(initialZoomLevel);
36
+
37
+ this.electronWindowPreferences.onPreferenceChanged(e => {
38
+ if (e.preferenceName === PREF_WINDOW_ZOOM_LEVEL) {
39
+ const zoomLevel = this.electronWindowPreferences.get(PREF_WINDOW_ZOOM_LEVEL, 0);
40
+ this.updateWindowZoomLevel(zoomLevel);
41
+ }
42
+ });
43
+ });
44
+ }
45
+
46
+ protected async updateWindowZoomLevel(zoomLevel: number): Promise<void> {
47
+ this.secondaryWindows.forEach((win: Window) => {
48
+ window.electronTheiaCore.setZoomLevel(zoomLevel, win.name);
49
+ });
50
+ }
51
+
25
52
  override focus(win: Window): void {
26
53
  window.electronTheiaCore.focusWindow(win.name);
27
54
  }
@@ -50,6 +77,10 @@ export class ElectronSecondaryWindowService extends DefaultSecondaryWindowServic
50
77
  window.electronTheiaCore.setMenuBarVisible(false, newWindow.name);
51
78
  window.electronTheiaCore.setSecondaryWindowCloseRequestHandler(newWindow.name, () => this.canClose(widget, shell, newWindow));
52
79
 
80
+ // Apply current zoom level to newly created secondary window
81
+ const currentZoomLevel = this.electronWindowPreferences.get(PREF_WINDOW_ZOOM_LEVEL, 0);
82
+ window.electronTheiaCore.setZoomLevel(currentZoomLevel, newWindow.name);
83
+
53
84
  // Below code may be used to debug contents of secondary window
54
85
  // window.electronTheiaCore.openDevToolsForWindow(newWindow.name);
55
86
  }
@@ -32,6 +32,8 @@ import { ExternalAppOpenHandler } from './external-app-open-handler';
32
32
  import { ElectronUriHandlerContribution } from '../electron-uri-handler';
33
33
  import { bindRootContributionProvider } from '../../common';
34
34
  import { WindowTitleContribution } from '../../browser/window/window-title-service';
35
+ import { WindowZoomStatusBarItem } from './window-zoom-status-bar-item';
36
+ import '../../../src/electron-browser/style/window-zoom-action-bar.css';
35
37
 
36
38
  export default new ContainerModule((bind, unbind, isBound, rebind) => {
37
39
  bind(ElectronMainWindowService).toDynamicValue(context =>
@@ -48,4 +50,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
48
50
  bind(ExternalAppOpenHandler).toSelf().inSingletonScope();
49
51
  bind(OpenHandler).toService(ExternalAppOpenHandler);
50
52
  bindRootContributionProvider(bind, WindowTitleContribution);
53
+ bind(WindowZoomStatusBarItem).toSelf().inSingletonScope();
54
+ bind(FrontendApplicationContribution).toService(WindowZoomStatusBarItem);
51
55
  });
@@ -15,10 +15,10 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { injectable, inject, postConstruct } from 'inversify';
18
- import { NewWindowOptions, WindowSearchParams } from '../../common/window';
18
+ import { NewWindowOptions } from '../../common/window';
19
19
  import { DefaultWindowService } from '../../browser/window/default-window-service';
20
20
  import { ElectronMainWindowService } from '../../electron-common/electron-main-window-service';
21
- import { ElectronWindowPreferences } from '../../electron-common/electron-window-preferences';
21
+ import { ElectronWindowPreferences, PREF_WINDOW_ZOOM_LEVEL } from '../../electron-common/electron-window-preferences';
22
22
  import { ConnectionCloseService } from '../../common/messaging/connection-management';
23
23
  import { FrontendIdProvider } from '../../browser/messaging/frontend-id-provider';
24
24
  import { WindowReloadOptions } from '../../browser/window/window-service';
@@ -57,8 +57,12 @@ export class ElectronWindowService extends DefaultWindowService {
57
57
  return undefined;
58
58
  }
59
59
 
60
- override openNewDefaultWindow(params?: WindowSearchParams): void {
61
- this.delegate.openNewDefaultWindow(params);
60
+ override async openNewDefaultWindow(params?: WindowReloadOptions): Promise<number> {
61
+ return this.delegate.openNewDefaultWindow(params?.search);
62
+ }
63
+
64
+ override closeWindow(windowId: number): void {
65
+ this.delegate.closeWindow(windowId);
62
66
  }
63
67
 
64
68
  override focus(): void {
@@ -68,7 +72,7 @@ export class ElectronWindowService extends DefaultWindowService {
68
72
  protected init(): void {
69
73
  // Update the default zoom level on startup when the preferences event is fired.
70
74
  this.electronWindowPreferences.onPreferenceChanged(e => {
71
- if (e.preferenceName === 'window.zoomLevel') {
75
+ if (e.preferenceName === PREF_WINDOW_ZOOM_LEVEL) {
72
76
  this.updateWindowZoomLevel();
73
77
  }
74
78
  });
@@ -94,7 +98,7 @@ export class ElectronWindowService extends DefaultWindowService {
94
98
  * Updates the window zoom level based on the preference value.
95
99
  */
96
100
  protected async updateWindowZoomLevel(): Promise<void> {
97
- const preferredZoomLevel = this.electronWindowPreferences['window.zoomLevel'];
101
+ const preferredZoomLevel = this.electronWindowPreferences[PREF_WINDOW_ZOOM_LEVEL];
98
102
  if (await window.electronTheiaCore.getZoomLevel() !== preferredZoomLevel) {
99
103
  window.electronTheiaCore.setZoomLevel(preferredZoomLevel);
100
104
  }
@@ -116,4 +120,3 @@ export class ElectronWindowService extends DefaultWindowService {
116
120
  }
117
121
  }
118
122
  }
119
-