1
>
/*---------------------------------------------------------------------------------------------
updateInfoParser.ts
2
>
* Copyright (c) Microsoft Corporation. All rights reserved.
3
>
* Licensed under the MIT License. See License.txt in the project root for license information.
4
>
*--------------------------------------------------------------------------------------------*/
5
>
6
>
import { hasKey, Mutable } from '../../../../base/common/types.js';
7
>
8
>
const MAX_FEATURES = 5;
9
>
10
>
export type UpdateInfoButtonStyle = 'primary' | 'secondary';
11
>
12
>
export interface IUpdateInfoButton {
13
>
readonly label: string;
14
>
readonly commandId: string;
15
>
readonly args?: unknown[];
16
>
readonly style?: UpdateInfoButtonStyle;
17
>
}
18
>
19
>
export interface IUpdateInfoFeature {
20
>
/**
21
>
* Optional Codicon icon identifier (e.g. `$(sparkle)` or `$(lightbulb)`) displayed
22
>
* alongside the feature title.
23
>
*/
24
>
readonly icon?: string;
25
>
/** Short title for the feature highlight. */
26
>
readonly title: string;
27
>
/** One-line description of the feature. */
28
>
readonly description: string;
29
>
}
30
>
31
>
export interface IParsedUpdateInfoInput {
32
>
/** Markdown body rendered in the update-info widget. */
33
>
readonly markdown: string;
34
>
/** Optional action buttons shown below the markdown content. */
35
>
readonly buttons?: IUpdateInfoButton[];
36
>
/**
37
>
* Optional URL for a banner/hero image shown at the top of the widget.
38
>
* Must be an `https://` URL; non-HTTPS URLs are ignored.
39
>
*/
40
>
readonly bannerImageUrl?: string;
41
>
/** Optional short badge label (e.g. `"New"`) displayed on the widget. */
42
>
readonly badge?: string;
43
>
/** Optional heading title rendered above the markdown body. */
44
>
readonly title?: string;
45
>
/**
46
>
* Optional list of feature highlights. At most {@link MAX_FEATURES} entries
47
>
* (currently 5) are displayed; any additional entries are silently dropped.
48
>
*/
49
>
readonly features?: IUpdateInfoFeature[];
50
>
}
51
>
52
>
/**
53
>
* Parses optional metadata from update info input.
54
>
*
55
>
* Supported formats:
56
>
*
57
>
* **JSON envelope** - a single JSON object with `markdown` and optional fields:
58
>
* ```json
59
>
* {
60
>
* "markdown": "$(info) **Feature**<br>Description...",
61
>
* "title": "What's New",
62
>
* "badge": "New",
63
>
* "bannerImageUrl": "https://example.com/banner.png",
64
>
* "buttons": [
65
>
* { "label": "Release Notes", "commandId": "update.showCurrentReleaseNotes", "style": "secondary" },
66
>
* { "label": "Open Sessions", "commandId": "workbench.action.chat.open", "style": "primary" }
67
>
* ],
68
>
* "features": [
69
>
* { "icon": "$(sparkle)", "title": "Feature", "description": "Short description" }
70
>
* ]
71
>
* }
72
>
* ```
73
>
*
74
>
* **Block frontmatter** - YAML-style `---` delimiters wrapping a JSON metadata block:
75
>
* ```
76
>
* ---
77
>
* { "buttons": [...], "features": [...] }
78
>
* ---
79
>
* $(info) **Feature**<br>Description...
80
>
* ```
81
>
*
82
>
* **Inline frontmatter** - metadata on a single `---` line:
83
>
* ```
84
>
* --- { "buttons": [...] } ---
85
>
* $(info) **Feature**<br>Description...
86
>
* ```
87
>
*
88
>
* At most 5 feature entries are retained; any additional ones are silently dropped.
89
>
*/
90
>
export function parseUpdateInfoInput(text: string): IParsedUpdateInfoInput {
91
>
const normalized = text.replace(/^\uFEFF/, '');
92
>
return tryParseUpdateInfoEnvelope(normalized) ?? parseUpdateInfoFrontmatter(normalized);
93
>
}
94
>
95
>
function tryParseUpdateInfoEnvelope(text: string): IParsedUpdateInfoInput | undefined {
96
>
const trimmed = text.trim();
97
>
if (!trimmed.startsWith('{') || !trimmed.endsWith('}')) {
98
return undefined;
99
}