@codingame/monaco-vscode-update-service-override 31.0.1 → 32.0.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codingame/monaco-vscode-update-service-override",
3
- "version": "31.0.1",
3
+ "version": "32.0.1",
4
4
  "private": false,
5
5
  "description": "VSCode public API plugged on the monaco editor - update service-override",
6
6
  "keywords": [],
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "type": "module",
17
17
  "dependencies": {
18
- "@codingame/monaco-vscode-api": "31.0.1"
18
+ "@codingame/monaco-vscode-api": "32.0.1"
19
19
  },
20
20
  "main": "index.js",
21
21
  "module": "index.js",
@@ -1,5 +1,5 @@
1
1
 
2
- import { isWindows, isWeb } from '@codingame/monaco-vscode-api/vscode/vs/base/common/platform';
2
+ import { isWeb, isWindows } from '@codingame/monaco-vscode-api/vscode/vs/base/common/platform';
3
3
  import { PolicyCategory } from '@codingame/monaco-vscode-api/vscode/vs/base/common/policy';
4
4
  import { localize } from '@codingame/monaco-vscode-api/vscode/vs/nls';
5
5
  import { Extensions, ConfigurationScope } from '@codingame/monaco-vscode-api/vscode/vs/platform/configuration/common/configurationRegistry';
@@ -9,7 +9,7 @@ const configurationRegistry = ( Registry.as(Extensions.Configuration));
9
9
  configurationRegistry.registerConfiguration({
10
10
  id: "update",
11
11
  order: 15,
12
- title: ( localize(2493, "Update")),
12
+ title: ( localize(2510, "Update")),
13
13
  type: "object",
14
14
  properties: {
15
15
  "update.mode": {
@@ -18,18 +18,18 @@ configurationRegistry.registerConfiguration({
18
18
  default: "default",
19
19
  scope: ConfigurationScope.APPLICATION,
20
20
  description: ( localize(
21
- 2494,
21
+ 2511,
22
22
  "Configure whether you receive automatic updates. Requires a restart after change. The updates are fetched from a Microsoft online service."
23
23
  )),
24
24
  tags: ["usesOnlineServices"],
25
- enumDescriptions: [( localize(2495, "Disable updates.")), ( localize(
26
- 2496,
25
+ enumDescriptions: [( localize(2512, "Disable updates.")), ( localize(
26
+ 2513,
27
27
  "Disable automatic background update checks. Updates will be available if you manually check for updates."
28
28
  )), ( localize(
29
- 2497,
29
+ 2514,
30
30
  "Check for updates only on startup. Disable automatic background update checks."
31
31
  )), ( localize(
32
- 2498,
32
+ 2515,
33
33
  "Enable automatic update checks. Code will check for updates automatically and periodically."
34
34
  ))],
35
35
  policy: {
@@ -40,29 +40,29 @@ configurationRegistry.registerConfiguration({
40
40
  description: {
41
41
  key: "updateMode",
42
42
  value: ( localize(
43
- 2494,
43
+ 2511,
44
44
  "Configure whether you receive automatic updates. Requires a restart after change. The updates are fetched from a Microsoft online service."
45
45
  ))
46
46
  },
47
47
  enumDescriptions: [{
48
48
  key: "none",
49
- value: ( localize(2495, "Disable updates."))
49
+ value: ( localize(2512, "Disable updates."))
50
50
  }, {
51
51
  key: "manual",
52
52
  value: ( localize(
53
- 2496,
53
+ 2513,
54
54
  "Disable automatic background update checks. Updates will be available if you manually check for updates."
55
55
  ))
56
56
  }, {
57
57
  key: "start",
58
58
  value: ( localize(
59
- 2497,
59
+ 2514,
60
60
  "Check for updates only on startup. Disable automatic background update checks."
61
61
  ))
62
62
  }, {
63
63
  key: "default",
64
64
  value: ( localize(
65
- 2498,
65
+ 2515,
66
66
  "Enable automatic update checks. Code will check for updates automatically and periodically."
67
67
  ))
68
68
  }]
@@ -74,11 +74,11 @@ configurationRegistry.registerConfiguration({
74
74
  default: "default",
75
75
  scope: ConfigurationScope.APPLICATION,
76
76
  description: ( localize(
77
- 2494,
77
+ 2511,
78
78
  "Configure whether you receive automatic updates. Requires a restart after change. The updates are fetched from a Microsoft online service."
79
79
  )),
80
80
  deprecationMessage: ( localize(
81
- 2499,
81
+ 2516,
82
82
  "This setting is deprecated, please use '{0}' instead.",
83
83
  "update.mode"
84
84
  ))
@@ -87,9 +87,9 @@ configurationRegistry.registerConfiguration({
87
87
  type: "boolean",
88
88
  default: true,
89
89
  scope: ConfigurationScope.APPLICATION,
90
- title: ( localize(2500, "Enable Background Updates")),
90
+ title: ( localize(2517, "Enable Background Updates")),
91
91
  description: ( localize(
92
- 2501,
92
+ 2518,
93
93
  "Enable to download and install new VS Code versions in the background."
94
94
  )),
95
95
  included: isWindows && !isWeb
@@ -99,20 +99,31 @@ configurationRegistry.registerConfiguration({
99
99
  default: true,
100
100
  scope: ConfigurationScope.APPLICATION,
101
101
  description: ( localize(
102
- 2502,
102
+ 2519,
103
103
  "Show Release Notes after an update. The Release Notes are fetched from a Microsoft online service."
104
104
  )),
105
- tags: ["usesOnlineServices"]
105
+ tags: ["usesOnlineServices"],
106
+ agentsWindow: {
107
+ default: false,
108
+ readOnly: true
109
+ }
106
110
  },
107
111
  "update.showPostInstallInfo": {
108
112
  type: "boolean",
109
- default: true,
113
+ default: false,
110
114
  scope: ConfigurationScope.APPLICATION,
111
115
  description: ( localize(
112
- 2503,
113
- "Show update information tooltip in the title bar after a new version is installed."
116
+ 2520,
117
+ "Show a post-install update tooltip in the title bar instead of opening the release notes editor."
114
118
  )),
115
- included: false
119
+ tags: ["usesOnlineServices"]
120
+ },
121
+ "update.titleBar": {
122
+ type: "boolean",
123
+ default: true,
124
+ scope: ConfigurationScope.APPLICATION,
125
+ description: ( localize(2521, "Show the update indicator in the title bar.")),
126
+ included: !isWeb
116
127
  }
117
128
  }
118
129
  });
@@ -123,11 +123,11 @@ let SimpleSettingRenderer = class SimpleSettingRenderer {
123
123
  }
124
124
  viewInSettingsMessage(settingId, alreadyDisplayed) {
125
125
  if (alreadyDisplayed) {
126
- return localize(10368, "View in Settings");
126
+ return localize(10691, "View in Settings");
127
127
  } else {
128
128
  const displayName = settingKeyToDisplayFormat(settingId);
129
129
  return localize(
130
- 10369,
130
+ 10692,
131
131
  "View \"{0}: {1}\" in Settings",
132
132
  displayName.category,
133
133
  displayName.label
@@ -137,7 +137,7 @@ let SimpleSettingRenderer = class SimpleSettingRenderer {
137
137
  restorePreviousSettingMessage(settingId) {
138
138
  const displayName = settingKeyToDisplayFormat(settingId);
139
139
  return localize(
140
- 10370,
140
+ 10693,
141
141
  "Restore value of \"{0}: {1}\"",
142
142
  displayName.category,
143
143
  displayName.label
@@ -152,14 +152,14 @@ let SimpleSettingRenderer = class SimpleSettingRenderer {
152
152
  if (this.isAlreadySet(setting, booleanValue)) {
153
153
  if (booleanValue) {
154
154
  return localize(
155
- 10371,
155
+ 10694,
156
156
  "\"{0}: {1}\" is already enabled",
157
157
  displayName.category,
158
158
  displayName.label
159
159
  );
160
160
  } else {
161
161
  return localize(
162
- 10372,
162
+ 10695,
163
163
  "\"{0}: {1}\" is already disabled",
164
164
  displayName.category,
165
165
  displayName.label
@@ -167,16 +167,16 @@ let SimpleSettingRenderer = class SimpleSettingRenderer {
167
167
  }
168
168
  }
169
169
  if (booleanValue) {
170
- return localize(10373, "Enable \"{0}: {1}\"", displayName.category, displayName.label);
170
+ return localize(10696, "Enable \"{0}: {1}\"", displayName.category, displayName.label);
171
171
  } else {
172
- return localize(10374, "Disable \"{0}: {1}\"", displayName.category, displayName.label);
172
+ return localize(10697, "Disable \"{0}: {1}\"", displayName.category, displayName.label);
173
173
  }
174
174
  }
175
175
  stringSettingMessage(setting, stringValue) {
176
176
  const displayName = settingKeyToDisplayFormat(setting.key);
177
177
  if (this.isAlreadySet(setting, stringValue)) {
178
178
  return localize(
179
- 10375,
179
+ 10698,
180
180
  "\"{0}: {1}\" is already set to \"{2}\"",
181
181
  displayName.category,
182
182
  displayName.label,
@@ -184,7 +184,7 @@ let SimpleSettingRenderer = class SimpleSettingRenderer {
184
184
  );
185
185
  }
186
186
  return localize(
187
- 10376,
187
+ 10699,
188
188
  "Set \"{0}: {1}\" to \"{2}\"",
189
189
  displayName.category,
190
190
  displayName.label,
@@ -195,7 +195,7 @@ let SimpleSettingRenderer = class SimpleSettingRenderer {
195
195
  const displayName = settingKeyToDisplayFormat(setting.key);
196
196
  if (this.isAlreadySet(setting, numberValue)) {
197
197
  return localize(
198
- 10377,
198
+ 10700,
199
199
  "\"{0}: {1}\" is already set to {2}",
200
200
  displayName.category,
201
201
  displayName.label,
@@ -203,7 +203,7 @@ let SimpleSettingRenderer = class SimpleSettingRenderer {
203
203
  );
204
204
  }
205
205
  return localize(
206
- 10378,
206
+ 10701,
207
207
  "Set \"{0}: {1}\" to {2}",
208
208
  displayName.category,
209
209
  displayName.label,
@@ -212,7 +212,7 @@ let SimpleSettingRenderer = class SimpleSettingRenderer {
212
212
  }
213
213
  renderSetting(setting, newValue) {
214
214
  const href = this.settingToUriString(setting.key, newValue);
215
- const title = ( localize(10379, "View or change setting"));
215
+ const title = ( localize(10702, "View or change setting"));
216
216
  return `<code tabindex="0"><a href="${href}" class="codesetting" title="${title}" aria-role="button"><svg width="14" height="14" viewBox="0 0 15 15" xmlns="http://www.w3.org/2000/svg" fill="currentColor"><path d="M9.1 4.4L8.6 2H7.4l-.5 2.4-.7.3-2-1.3-.9.8 1.3 2-.2.7-2.4.5v1.2l2.4.5.3.8-1.3 2 .8.8 2-1.3.8.3.4 2.3h1.2l.5-2.4.8-.3 2 1.3.8-.8-1.3-2 .3-.8 2.3-.4V7.4l-2.4-.5-.3-.8 1.3-2-.8-.8-2 1.3-.7-.2zM9.4 1l.5 2.4L12 2.1l2 2-1.4 2.1 2.4.4v2.8l-2.4.5L14 12l-2 2-2.1-1.4-.5 2.4H6.6l-.5-2.4L4 13.9l-2-2 1.4-2.1L1 9.4V6.6l2.4-.5L2.1 4l2-2 2.1 1.4.4-2.4h2.8zm.6 7c0 1.1-.9 2-2 2s-2-.9-2-2 .9-2 2-2 2 .9 2 2zM8 9c.6 0 1-.4 1-1s-.4-1-1-1-1 .4-1 1 .4 1 1 1z"/></svg>
217
217
  <span class="separator"></span>
218
218
  <span class="setting-name">${setting.key}</span>
@@ -290,8 +290,8 @@ let SimpleSettingRenderer = class SimpleSettingRenderer {
290
290
  class: undefined,
291
291
  enabled: true,
292
292
  id: "copySettingId",
293
- tooltip: ( localize(10380, "Copy Setting ID")),
294
- label: ( localize(10380, "Copy Setting ID")),
293
+ tooltip: ( localize(10703, "Copy Setting ID")),
294
+ label: ( localize(10703, "Copy Setting ID")),
295
295
  run: () => {
296
296
  this._clipboardService.writeText(settingId);
297
297
  }
@@ -3,27 +3,140 @@
3
3
  * Licensed under the MIT License. See License.txt in the project root for license information.
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
 
6
+ /*
7
+ * The widget supplies its own padding and border-radius via `.post-update-widget`,
8
+ * so we strip the compact hover's contents padding. The selector below is at least as
9
+ * specific as `.monaco-hover.workbench-hover.compact .hover-contents` and loads after it,
10
+ * so it wins without `!important`.
11
+ */
12
+ .monaco-hover.workbench-hover.post-update-widget-hover .hover-contents {
13
+ padding: 0;
14
+ }
15
+
6
16
  .post-update-widget {
17
+ position: relative;
7
18
  display: flex;
8
19
  flex-direction: column;
9
- gap: 12px;
10
- padding: 6px 6px;
11
- min-width: 300px;
12
- max-width: 550px;
20
+ min-width: 320px;
21
+ max-width: 420px;
13
22
  color: var(--vscode-descriptionForeground);
14
23
  font-size: var(--vscode-bodyFontSize-small);
24
+ border-radius: var(--vscode-cornerRadius-medium);
25
+ overflow: hidden;
26
+ }
27
+
28
+ /* Banner */
29
+ .post-update-widget .banner {
30
+ position: relative;
31
+ aspect-ratio: 16 / 5;
32
+ min-height: 90px;
33
+ background-position: center;
34
+ background-size: cover;
35
+ background-repeat: no-repeat;
36
+ /* Default: VS Code "copilot free" gradient (recreated with layered radial gradients) */
37
+ background-color: #0b1020;
38
+ background-image:
39
+ radial-gradient(ellipse 60% 80% at 12% 90%, rgba(132, 204, 22, 0.55), transparent 60%),
40
+ radial-gradient(ellipse 70% 90% at 25% 30%, rgba(56, 189, 248, 0.45), transparent 65%),
41
+ radial-gradient(ellipse 90% 100% at 75% 60%, rgba(99, 102, 241, 0.55), transparent 70%),
42
+ radial-gradient(ellipse 60% 80% at 100% 100%, rgba(30, 27, 75, 0.7), transparent 70%),
43
+ linear-gradient(135deg, #0b1020 0%, #1e1b4b 100%);
44
+ }
45
+
46
+ .post-update-widget .banner-close {
47
+ position: absolute;
48
+ top: 8px;
49
+ right: 8px;
50
+ width: 22px;
51
+ height: 22px;
52
+ display: flex;
53
+ align-items: center;
54
+ justify-content: center;
55
+ background: transparent;
56
+ color: var(--vscode-icon-foreground);
57
+ border: none;
58
+ border-radius: var(--vscode-cornerRadius-small);
59
+ cursor: pointer;
60
+ padding: 0;
61
+ z-index: 1;
62
+ }
63
+
64
+ .post-update-widget .banner-close:hover {
65
+ background-color: var(--vscode-toolbar-hoverBackground);
66
+ }
67
+
68
+ /* Body */
69
+ .post-update-widget .body {
70
+ display: flex;
71
+ flex-direction: column;
72
+ gap: 14px;
73
+ padding: 16px;
74
+ }
75
+
76
+ /* Badge */
77
+ .post-update-widget .badge {
78
+ align-self: flex-start;
79
+ padding: 2px 8px;
80
+ border-radius: var(--vscode-cornerRadius-small);
81
+ background-color: var(--vscode-extensionBadge-remoteBackground, var(--vscode-badge-background));
82
+ color: var(--vscode-extensionBadge-remoteForeground, var(--vscode-badge-foreground));
83
+ font-size: 11px;
84
+ font-weight: 600;
85
+ line-height: 1.4;
86
+ text-transform: none;
87
+ }
88
+
89
+ /* Title */
90
+ .post-update-widget .title {
91
+ font-weight: 600;
92
+ font-size: 16px;
93
+ line-height: 1.3;
94
+ color: var(--vscode-foreground);
95
+ }
96
+
97
+ /* Features */
98
+ .post-update-widget .features {
99
+ display: flex;
100
+ flex-direction: column;
101
+ gap: 12px;
15
102
  }
16
103
 
17
- /* Header */
18
- .post-update-widget .header {
104
+ .post-update-widget .feature {
105
+ display: grid;
106
+ grid-template-columns: 20px 1fr;
107
+ column-gap: 12px;
108
+ align-items: start;
109
+ }
110
+
111
+ .post-update-widget .feature-icon {
19
112
  display: flex;
20
- justify-content: space-between;
21
113
  align-items: center;
114
+ justify-content: center;
115
+ color: var(--vscode-foreground);
116
+ margin-top: 2px;
117
+ font-size: 16px;
118
+ }
119
+
120
+ .post-update-widget .feature-text {
121
+ display: flex;
122
+ flex-direction: column;
123
+ gap: 2px;
22
124
  }
23
125
 
24
- .post-update-widget .header .title {
126
+ .post-update-widget .feature-title {
25
127
  font-weight: 600;
26
- font-size: var(--vscode-bodyFontSize);
128
+ color: var(--vscode-foreground);
129
+ font-size: var(--vscode-bodyFontSize-small);
130
+ }
131
+
132
+ .post-update-widget .feature-description {
133
+ color: var(--vscode-descriptionForeground);
134
+ font-size: var(--vscode-bodyFontSize-small);
135
+ line-height: 1.4;
136
+ }
137
+
138
+ /* Markdown fallback */
139
+ .post-update-widget .update-markdown {
27
140
  color: var(--vscode-foreground);
28
141
  }
29
142
 
@@ -33,12 +146,14 @@
33
146
  justify-content: flex-end;
34
147
  align-items: center;
35
148
  gap: 8px;
149
+ margin-top: 4px;
36
150
  }
37
151
 
38
152
  .post-update-widget .button-bar button {
39
- padding: 4px 12px;
153
+ padding: 8px 16px;
40
154
  border-radius: var(--vscode-cornerRadius-small);
41
155
  font-size: var(--vscode-bodyFontSize-small);
156
+ font-weight: 600;
42
157
  cursor: pointer;
43
158
  white-space: nowrap;
44
159
  }
@@ -66,3 +181,9 @@
66
181
  .post-update-widget .update-button-primary:hover {
67
182
  background-color: var(--vscode-button-hoverBackground);
68
183
  }
184
+
185
+ .post-update-widget .update-button-full-width {
186
+ width: 100%;
187
+ justify-content: center;
188
+ text-align: center;
189
+ }
@@ -26,6 +26,7 @@ export declare class PostUpdateWidgetContribution extends Disposable implements
26
26
  private readonly requestService;
27
27
  private readonly storageService;
28
28
  private readonly telemetryService;
29
+ private static idCounter;
29
30
  constructor(commandService: ICommandService, configurationService: IConfigurationService, hostService: IHostService, hoverService: IHoverService, layoutService: ILayoutService, markdownRendererService: IMarkdownRendererService, openerService: IOpenerService, productService: IProductService, requestService: IRequestService, storageService: IStorageService, telemetryService: ITelemetryService);
30
31
  private tryShowOnStartup;
31
32
  private showUpdateInfo;
@@ -25,11 +25,21 @@ import { IHostService } from '@codingame/monaco-vscode-api/vscode/vs/workbench/s
25
25
  import { ShowCurrentReleaseNotesActionId } from '@codingame/monaco-vscode-api/vscode/vs/workbench/contrib/update/common/update';
26
26
  import { parseUpdateInfoInput } from '../common/updateInfoParser.js';
27
27
  import { getUpdateInfoUrl, isMajorMinorVersionChange } from '../common/updateUtils.js';
28
+ import { ThemeIcon } from '@codingame/monaco-vscode-api/vscode/vs/base/common/themables';
29
+ import { Codicon } from '@codingame/monaco-vscode-api/vscode/vs/base/common/codicons';
30
+ import { URI } from '@codingame/monaco-vscode-api/vscode/vs/base/common/uri';
28
31
  import * as postUpdateWidget from './media/postUpdateWidget.css';
29
32
 
33
+ var PostUpdateWidgetContribution_1;
30
34
  registerCss(postUpdateWidget);
31
35
  const LAST_KNOWN_VERSION_KEY = "postUpdateWidget/lastKnownVersion";
32
36
  let PostUpdateWidgetContribution = class PostUpdateWidgetContribution extends Disposable {
37
+ static {
38
+ PostUpdateWidgetContribution_1 = this;
39
+ }
40
+ static {
41
+ this.idCounter = 0;
42
+ }
33
43
  constructor(
34
44
  commandService,
35
45
  configurationService,
@@ -86,7 +96,7 @@ let PostUpdateWidgetContribution = class PostUpdateWidgetContribution extends Di
86
96
  const {
87
97
  clientWidth
88
98
  } = target;
89
- const maxWidth = 550;
99
+ const maxWidth = 420;
90
100
  const x = Math.max(clientWidth - maxWidth - 80, 16);
91
101
  this.hoverService.showInstantHover({
92
102
  content: this.buildContent(info, contentDisposables),
@@ -96,14 +106,16 @@ let PostUpdateWidgetContribution = class PostUpdateWidgetContribution extends Di
96
106
  y: 40,
97
107
  dispose: () => contentDisposables.dispose()
98
108
  },
109
+ additionalClasses: ["post-update-widget-hover"],
99
110
  persistence: {
100
111
  sticky: true
101
112
  },
102
113
  appearance: {
103
114
  showPointer: false,
104
115
  compact: true,
105
- maxHeightRatio: 0.8
106
- }
116
+ maxHeightRatio: 1
117
+ },
118
+ trapFocus: true
107
119
  }, true);
108
120
  }
109
121
  async getUpdateInfo(input) {
@@ -125,7 +137,7 @@ let PostUpdateWidgetContribution = class PostUpdateWidgetContribution extends Di
125
137
  info = {
126
138
  ...info,
127
139
  buttons: [{
128
- label: ( localize(14961, "Release Notes")),
140
+ label: ( localize(15291, "Release Notes")),
129
141
  commandId: ShowCurrentReleaseNotesActionId,
130
142
  args: [this.productService.version],
131
143
  style: "secondary"
@@ -134,31 +146,83 @@ let PostUpdateWidgetContribution = class PostUpdateWidgetContribution extends Di
134
146
  }
135
147
  return info;
136
148
  }
137
- buildContent(
138
- {
149
+ buildContent(info, disposables) {
150
+ const {
139
151
  markdown,
140
- buttons
141
- },
142
- disposables
143
- ) {
152
+ buttons,
153
+ bannerImageUrl,
154
+ badge,
155
+ title,
156
+ features
157
+ } = info;
144
158
  const container = $(".post-update-widget");
145
- const header = append(container, $(".header"));
146
- const title = append(header, $(".title"));
147
- title.textContent = ( localize(14962, "New in {0}", this.productService.version));
148
- const markdownContainer = append(container, $(".update-markdown"));
149
- const rendered = disposables.add(this.markdownRendererService.render(( new MarkdownString(markdown, {
150
- isTrusted: true,
151
- supportHtml: true,
152
- supportThemeIcons: true
153
- })), {
154
- actionHandler: (link, mdStr) => {
155
- openLinkFromMarkdown(this.openerService, link, mdStr.isTrusted);
156
- this.hoverService.hideHover(true);
157
- }
159
+ const titleId = `post-update-widget-title-${PostUpdateWidgetContribution_1.idCounter++}`;
160
+ container.setAttribute("role", "dialog");
161
+ container.setAttribute("aria-labelledby", titleId);
162
+ const banner = append(container, $(".banner"));
163
+ banner.setAttribute("aria-hidden", "true");
164
+ const safeBannerUrl = sanitizeBannerImageUrl(bannerImageUrl);
165
+ if (safeBannerUrl) {
166
+ banner.style.setProperty("background-image", `url(${JSON.stringify(safeBannerUrl)})`);
167
+ }
168
+ const closeButton = append(container, $("button.banner-close"));
169
+ closeButton.setAttribute("aria-label", ( localize(15292, "Close")));
170
+ const closeIcon = append(closeButton, $(ThemeIcon.asCSSSelector(Codicon.close)));
171
+ closeIcon.setAttribute("aria-hidden", "true");
172
+ disposables.add(addDisposableListener(closeButton, "click", () => {
173
+ this.hoverService.hideHover(true);
158
174
  }));
159
- markdownContainer.appendChild(rendered.element);
175
+ const body = append(container, $(".body"));
176
+ if (badge) {
177
+ const badgeEl = append(body, $(".badge"));
178
+ badgeEl.textContent = badge;
179
+ }
180
+ const titleEl = append(body, $(".title"));
181
+ titleEl.id = titleId;
182
+ titleEl.textContent = title ?? ( localize(15293, "New in {0}", this.productService.version));
183
+ if (features?.length) {
184
+ const list = append(body, $(".features"));
185
+ list.setAttribute("role", "list");
186
+ for (const feature of features) {
187
+ const row = append(list, $(".feature"));
188
+ row.setAttribute("role", "listitem");
189
+ const iconEl = append(row, $(".feature-icon"));
190
+ const iconId = feature.icon ?? Codicon.sparkle.id;
191
+ const themeIcon = ThemeIcon.fromId(iconId);
192
+ iconEl.classList.add(...ThemeIcon.asClassNameArray(themeIcon));
193
+ iconEl.setAttribute("aria-hidden", "true");
194
+ const text = append(row, $(".feature-text"));
195
+ const featureTitle = append(text, $(".feature-title"));
196
+ featureTitle.textContent = feature.title;
197
+ const featureDescription = append(text, $(".feature-description"));
198
+ const rendered = disposables.add(this.markdownRendererService.render(( new MarkdownString(feature.description, {
199
+ isTrusted: true,
200
+ supportThemeIcons: true
201
+ })), {
202
+ actionHandler: (link, mdStr) => {
203
+ openLinkFromMarkdown(this.openerService, link, mdStr.isTrusted);
204
+ this.hoverService.hideHover(true);
205
+ }
206
+ }));
207
+ featureDescription.appendChild(rendered.element);
208
+ }
209
+ } else if (markdown) {
210
+ const markdownContainer = append(body, $(".update-markdown"));
211
+ const rendered = disposables.add(this.markdownRendererService.render(( new MarkdownString(markdown, {
212
+ isTrusted: true,
213
+ supportHtml: true,
214
+ supportThemeIcons: true
215
+ })), {
216
+ actionHandler: (link, mdStr) => {
217
+ openLinkFromMarkdown(this.openerService, link, mdStr.isTrusted);
218
+ this.hoverService.hideHover(true);
219
+ }
220
+ }));
221
+ markdownContainer.appendChild(rendered.element);
222
+ }
160
223
  if (buttons?.length) {
161
- const buttonBar = append(container, $(".button-bar"));
224
+ const buttonBar = append(body, $(".button-bar"));
225
+ const isSingleButton = buttons.length === 1;
162
226
  let seenSecondary = false;
163
227
  for (const {
164
228
  label,
@@ -177,6 +241,9 @@ let PostUpdateWidgetContribution = class PostUpdateWidgetContribution extends Di
177
241
  } else {
178
242
  button.classList.add("update-button-primary");
179
243
  }
244
+ if (isSingleButton) {
245
+ button.classList.add("update-button-full-width");
246
+ }
180
247
  disposables.add(addDisposableListener(button, "click", () => {
181
248
  this.telemetryService.publicLog2("workbenchActionExecuted", {
182
249
  id: commandId,
@@ -214,6 +281,21 @@ let PostUpdateWidgetContribution = class PostUpdateWidgetContribution extends Di
214
281
  return false;
215
282
  }
216
283
  };
217
- PostUpdateWidgetContribution = ( __decorate([( __param(0, ICommandService)), ( __param(1, IConfigurationService)), ( __param(2, IHostService)), ( __param(3, IHoverService)), ( __param(4, ILayoutService)), ( __param(5, IMarkdownRendererService)), ( __param(6, IOpenerService)), ( __param(7, IProductService)), ( __param(8, IRequestService)), ( __param(9, IStorageService)), ( __param(10, ITelemetryService))], PostUpdateWidgetContribution));
284
+ PostUpdateWidgetContribution = PostUpdateWidgetContribution_1 = ( __decorate([( __param(0, ICommandService)), ( __param(1, IConfigurationService)), ( __param(2, IHostService)), ( __param(3, IHoverService)), ( __param(4, ILayoutService)), ( __param(5, IMarkdownRendererService)), ( __param(6, IOpenerService)), ( __param(7, IProductService)), ( __param(8, IRequestService)), ( __param(9, IStorageService)), ( __param(10, ITelemetryService))], PostUpdateWidgetContribution));
285
+ function sanitizeBannerImageUrl(value) {
286
+ if (!value) {
287
+ return undefined;
288
+ }
289
+ try {
290
+ const uri = ( URI.parse(value, true));
291
+ if (uri.scheme === "https") {
292
+ return ( uri.toString(true));
293
+ }
294
+ if (uri.scheme === "data" && /^image\//i.test(uri.path)) {
295
+ return ( uri.toString(true));
296
+ }
297
+ } catch {}
298
+ return undefined;
299
+ }
218
300
 
219
301
  export { PostUpdateWidgetContribution };
@@ -104,7 +104,7 @@ let ReleaseNotesManager = class ReleaseNotesManager extends Disposable {
104
104
  base
105
105
  };
106
106
  const html = await this.renderBody(this._lastMeta);
107
- const title = ( localize(14963, "Release Notes: {0}", version));
107
+ const title = ( localize(15294, "Release Notes: {0}", version));
108
108
  const activeEditorPane = this._editorService.activeEditorPane;
109
109
  if (this._currentReleaseNotes) {
110
110
  this._currentReleaseNotes.setWebviewTitle(title);
@@ -160,7 +160,7 @@ let ReleaseNotesManager = class ReleaseNotesManager extends Disposable {
160
160
  const versionLabel = match[1].replace(/\./g, "_");
161
161
  const baseUrl = "https://code.visualstudio.com/raw";
162
162
  const url = `${baseUrl}/v${versionLabel}.md`;
163
- const unassigned = ( localize(14964, "unassigned"));
163
+ const unassigned = ( localize(15295, "unassigned"));
164
164
  const escapeMdHtml = text => {
165
165
  return escape(text).replace(/\\/g, "\\\\");
166
166
  };
@@ -542,7 +542,7 @@ let ReleaseNotesManager = class ReleaseNotesManager extends Disposable {
542
542
 
543
543
  const label = document.createElement('label');
544
544
  label.htmlFor = 'showReleaseNotes';
545
- label.textContent = '${( localize(14965, "Show release notes after an update"))}';
545
+ label.textContent = '${( localize(15296, "Show release notes after an update"))}';
546
546
  container.appendChild(label);
547
547
 
548
548
  const beforeElement = document.querySelector("body > h1")?.nextElementSibling;