chrome-devtools-frontend 1.0.1510848 → 1.0.1512349
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/front_end/Images/src/ai-explorer-badge.svg +114 -0
- package/front_end/Images/src/code-whisperer-badge.svg +166 -0
- package/front_end/Images/src/devtools-user-badge.svg +129 -0
- package/front_end/Images/src/dom-detective-badge.svg +136 -0
- package/front_end/Images/src/speedster-badge.svg +166 -0
- package/front_end/core/host/AidaClient.ts +2 -0
- package/front_end/core/host/GdpClient.ts +38 -2
- package/front_end/core/i18n/NumberFormatter.ts +7 -0
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +3 -19
- package/front_end/models/ai_assistance/ai_assistance.ts +1 -1
- package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +7 -6
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +119 -119
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +43 -52
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +100 -100
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +12 -18
- package/front_end/models/ai_assistance/data_formatters/UnitFormatters.ts +151 -0
- package/front_end/models/ai_code_completion/AiCodeCompletion.ts +3 -0
- package/front_end/models/badges/Badge.ts +7 -4
- package/front_end/models/badges/DOMDetectiveBadge.ts +20 -0
- package/front_end/models/badges/SpeedsterBadge.ts +4 -1
- package/front_end/models/badges/StarterBadge.ts +5 -1
- package/front_end/models/badges/UserBadges.ts +33 -7
- package/front_end/models/trace/ModelImpl.ts +0 -13
- package/front_end/models/trace/insights/Common.ts +19 -0
- package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +36 -9
- package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +32 -0
- package/front_end/panels/common/AiCodeCompletionTeaser.ts +14 -2
- package/front_end/panels/common/BadgeNotification.ts +119 -9
- package/front_end/panels/common/badgeNotification.css +4 -0
- package/front_end/panels/console/ConsolePrompt.ts +26 -0
- package/front_end/panels/elements/ElementsTreeElement.ts +12 -0
- package/front_end/panels/elements/ElementsTreeOutline.ts +3 -0
- package/front_end/panels/elements/StylePropertiesSection.ts +3 -0
- package/front_end/panels/elements/StylePropertyTreeElement.ts +5 -0
- package/front_end/panels/settings/SettingsScreen.ts +3 -9
- package/front_end/panels/settings/components/SyncSection.ts +6 -2
- package/front_end/panels/sources/AiCodeCompletionPlugin.ts +35 -6
- package/front_end/panels/timeline/TimelinePanel.ts +22 -10
- package/front_end/panels/timeline/TimelineUIUtils.ts +4 -3
- package/front_end/panels/timeline/utils/InsightAIContext.ts +0 -19
- package/front_end/ui/legacy/filter.css +1 -1
- package/front_end/ui/legacy/inspectorCommon.css +1 -1
- package/front_end/ui/legacy/softDropDownButton.css +1 -1
- package/package.json +1 -1
- package/front_end/models/ai_assistance/data_formatters/Types.ts +0 -9
@@ -2,7 +2,9 @@
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
3
3
|
// found in the LICENSE file.
|
4
4
|
|
5
|
+
import * as Host from '../../core/host/host.js';
|
5
6
|
import * as i18n from '../../core/i18n/i18n.js';
|
7
|
+
import * as Badges from '../../models/badges/badges.js';
|
6
8
|
import * as Buttons from '../../ui/components/buttons/buttons.js';
|
7
9
|
import * as UI from '../../ui/legacy/legacy.js';
|
8
10
|
import * as Lit from '../../ui/lit/lit.js';
|
@@ -17,10 +19,50 @@ const UIStrings = {
|
|
17
19
|
* @description Title for close button
|
18
20
|
*/
|
19
21
|
dismiss: 'Dismiss',
|
22
|
+
/**
|
23
|
+
* @description Activity based badge award notification text
|
24
|
+
* @example {Badge Title} PH1
|
25
|
+
*/
|
26
|
+
activityBasedBadgeAwardMessage: 'You earned the {PH1} badge! It has been added to your Developer Profile.',
|
27
|
+
/**
|
28
|
+
* @description Action title for navigating to the badge settings in Google Developer Profile section
|
29
|
+
*/
|
30
|
+
badgeSettings: 'Badge settings',
|
31
|
+
/**
|
32
|
+
* @description Action title for opening the Google Developer Program profile page of the user in a new tab
|
33
|
+
*/
|
34
|
+
viewProfile: 'View profile',
|
35
|
+
/**
|
36
|
+
* @description Starter badge award notification text when the user has a Google Developer Program profile but did not enable receiving badges in DevTools yet
|
37
|
+
* @example {Badge Title} PH1
|
38
|
+
* @example {Google Developer Program link} PH2
|
39
|
+
*/
|
40
|
+
starterBadgeAwardMessageSettingDisabled: 'You earned the {PH1} badge for the {PH2}! Turn on badges to claim it.',
|
41
|
+
/**
|
42
|
+
* @description Starter badge award notification text when the user does not have a Google Developer Program profile.
|
43
|
+
* @example {Badge Title} PH1
|
44
|
+
* @example {Google Developer Program link} PH2
|
45
|
+
*/
|
46
|
+
starterBadgeAwardMessageNoGdpProfile:
|
47
|
+
'You earned the {PH1} badge for the {PH2}! Create a profile to claim your badge.',
|
48
|
+
/**
|
49
|
+
* @description Action title for snoozing the starter badge.
|
50
|
+
*/
|
51
|
+
remindMeLater: 'Remind me later',
|
52
|
+
/**
|
53
|
+
* @description Action title for enabling the "Receive badges" setting
|
54
|
+
*/
|
55
|
+
receiveBadges: 'Receive badges',
|
56
|
+
/**
|
57
|
+
* @description Action title for creating a Google Developer Program profle
|
58
|
+
*/
|
59
|
+
createProfile: 'Create profile',
|
20
60
|
} as const;
|
21
61
|
|
22
62
|
const str_ = i18n.i18n.registerUIStrings('panels/common/BadgeNotification.ts', UIStrings);
|
23
63
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
64
|
+
const i18nFormatString = i18n.i18n.getFormatLocalizedString.bind(undefined, str_);
|
65
|
+
const lockedString = i18n.i18n.lockedString;
|
24
66
|
|
25
67
|
export interface BadgeNotificationAction {
|
26
68
|
label: string;
|
@@ -30,7 +72,7 @@ export interface BadgeNotificationAction {
|
|
30
72
|
}
|
31
73
|
|
32
74
|
export interface BadgeNotificationProperties {
|
33
|
-
message:
|
75
|
+
message: HTMLElement|string;
|
34
76
|
imageUri: string;
|
35
77
|
actions: BadgeNotificationAction[];
|
36
78
|
}
|
@@ -80,7 +122,7 @@ const DEFAULT_VIEW = (input: ViewInput, _output: undefined, target: HTMLElement)
|
|
80
122
|
type View = typeof DEFAULT_VIEW;
|
81
123
|
|
82
124
|
export class BadgeNotification extends UI.Widget.Widget {
|
83
|
-
message:
|
125
|
+
message: HTMLElement|string = '';
|
84
126
|
imageUri = '';
|
85
127
|
actions: BadgeNotificationAction[] = [];
|
86
128
|
|
@@ -91,13 +133,81 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
91
133
|
this.#view = view;
|
92
134
|
}
|
93
135
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
136
|
+
async present(badge: Badges.Badge): Promise<void> {
|
137
|
+
if (badge.isStarterBadge) {
|
138
|
+
await this.#presentStarterBadge(badge);
|
139
|
+
} else {
|
140
|
+
this.#presentActivityBasedBadge(badge);
|
141
|
+
}
|
142
|
+
}
|
143
|
+
|
144
|
+
#show(properties: BadgeNotificationProperties): void {
|
145
|
+
this.message = properties.message;
|
146
|
+
this.imageUri = properties.imageUri;
|
147
|
+
this.actions = properties.actions;
|
148
|
+
this.requestUpdate();
|
149
|
+
this.show(UI.InspectorView.InspectorView.instance().element);
|
150
|
+
}
|
151
|
+
|
152
|
+
async #presentStarterBadge(badge: Badges.Badge): Promise<void> {
|
153
|
+
const gdpProfile = await Host.GdpClient.GdpClient.instance().getProfile();
|
154
|
+
const receiveBadgesSettingEnabled = Badges.UserBadges.instance().isReceiveBadgesSettingEnabled();
|
155
|
+
const googleDeveloperProgramLink = UI.XLink.XLink.create(
|
156
|
+
'https://developers.google.com/program', lockedString('Google Developer Program'), 'badge-link', undefined,
|
157
|
+
'gdp.program-link');
|
158
|
+
|
159
|
+
// If the user already has a GDP profile and the receive badges setting enabled,
|
160
|
+
// starter badge behaves as if it's an activity based badge.
|
161
|
+
if (gdpProfile && receiveBadgesSettingEnabled) {
|
162
|
+
this.#presentActivityBasedBadge(badge);
|
163
|
+
return;
|
164
|
+
}
|
165
|
+
|
166
|
+
// If the user already has a GDP profile and the receive badges setting disabled,
|
167
|
+
// starter badge behaves as a nudge for opting into receiving badges.
|
168
|
+
if (gdpProfile && !receiveBadgesSettingEnabled) {
|
169
|
+
this.#show({
|
170
|
+
message: i18nFormatString(
|
171
|
+
UIStrings.starterBadgeAwardMessageSettingDisabled, {PH1: badge.title, PH2: googleDeveloperProgramLink}),
|
172
|
+
actions: [
|
173
|
+
{
|
174
|
+
label: i18nString(UIStrings.remindMeLater),
|
175
|
+
onClick: () => {/* To implement */},
|
176
|
+
},
|
177
|
+
{label: i18nString(UIStrings.receiveBadges), onClick: () => { /* To implement */ }}
|
178
|
+
],
|
179
|
+
imageUri: badge.imageUri,
|
180
|
+
});
|
181
|
+
return;
|
182
|
+
}
|
183
|
+
|
184
|
+
// The user does not have a GDP profile, starter badge acts as a nudge for creating a GDP profile.
|
185
|
+
this.#show({
|
186
|
+
message: i18nFormatString(
|
187
|
+
UIStrings.starterBadgeAwardMessageNoGdpProfile, {PH1: badge.title, PH2: googleDeveloperProgramLink}),
|
188
|
+
actions: [
|
189
|
+
{
|
190
|
+
label: i18nString(UIStrings.remindMeLater),
|
191
|
+
onClick: () => {/* TODO(ergunsh): Implement */},
|
192
|
+
},
|
193
|
+
{label: i18nString(UIStrings.createProfile), onClick: () => { /* TODO(ergunsh): Implement */ }}
|
194
|
+
],
|
195
|
+
imageUri: badge.imageUri,
|
196
|
+
});
|
197
|
+
}
|
198
|
+
|
199
|
+
#presentActivityBasedBadge(badge: Badges.Badge): void {
|
200
|
+
this.#show({
|
201
|
+
message: i18nString(UIStrings.activityBasedBadgeAwardMessage, {PH1: badge.title}),
|
202
|
+
actions: [
|
203
|
+
{
|
204
|
+
label: i18nString(UIStrings.badgeSettings),
|
205
|
+
onClick: () => {/* TODO(ergunsh): Implement */},
|
206
|
+
},
|
207
|
+
{label: i18nString(UIStrings.viewProfile), onClick: () => { /* TODO(ergunsh): Implement */ }}
|
208
|
+
],
|
209
|
+
imageUri: badge.imageUri,
|
210
|
+
});
|
101
211
|
}
|
102
212
|
|
103
213
|
#close = (): void => {
|
@@ -71,6 +71,8 @@ export class ConsolePrompt extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
71
71
|
#javaScriptCompletionCompartment: CodeMirror.Compartment = new CodeMirror.Compartment();
|
72
72
|
|
73
73
|
private aidaClient?: Host.AidaClient.AidaClient;
|
74
|
+
private aidaAvailability?: Host.AidaClient.AidaAccessPreconditions;
|
75
|
+
private boundOnAidaAvailabilityChange?: () => Promise<void>;
|
74
76
|
private aiCodeCompletion?: AiCodeCompletion.AiCodeCompletion.AiCodeCompletion;
|
75
77
|
private teaser?: PanelCommon.AiCodeCompletionTeaser;
|
76
78
|
private placeholderCompartment: CodeMirror.Compartment = new CodeMirror.Compartment();
|
@@ -199,6 +201,10 @@ export class ConsolePrompt extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
199
201
|
if (this.isAiCodeCompletionEnabled()) {
|
200
202
|
this.aiCodeCompletionSetting.addChangeListener(this.onAiCodeCompletionSettingChanged.bind(this));
|
201
203
|
this.onAiCodeCompletionSettingChanged();
|
204
|
+
this.boundOnAidaAvailabilityChange = this.onAidaAvailabilityChange.bind(this);
|
205
|
+
Host.AidaClient.HostConfigTracker.instance().addEventListener(
|
206
|
+
Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.boundOnAidaAvailabilityChange);
|
207
|
+
void this.onAidaAvailabilityChange();
|
202
208
|
}
|
203
209
|
}
|
204
210
|
|
@@ -294,6 +300,10 @@ export class ConsolePrompt extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
294
300
|
this.highlightingNode = false;
|
295
301
|
SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight();
|
296
302
|
}
|
303
|
+
if (this.boundOnAidaAvailabilityChange) {
|
304
|
+
Host.AidaClient.HostConfigTracker.instance().removeEventListener(
|
305
|
+
Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.boundOnAidaAvailabilityChange);
|
306
|
+
}
|
297
307
|
}
|
298
308
|
|
299
309
|
history(): TextEditor.AutocompleteHistory.AutocompleteHistory {
|
@@ -505,6 +515,9 @@ export class ConsolePrompt extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
505
515
|
// TODO(b/435654172): Refactor and move aiCodeCompletion model one level up to avoid
|
506
516
|
// defining additional listeners and events.
|
507
517
|
private setAiCodeCompletion(): void {
|
518
|
+
if (this.aiCodeCompletion) {
|
519
|
+
return;
|
520
|
+
}
|
508
521
|
if (!this.aidaClient) {
|
509
522
|
this.aidaClient = new Host.AidaClient.AidaClient();
|
510
523
|
}
|
@@ -532,6 +545,19 @@ export class ConsolePrompt extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
532
545
|
}
|
533
546
|
}
|
534
547
|
|
548
|
+
private async onAidaAvailabilityChange(): Promise<void> {
|
549
|
+
const currentAidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
|
550
|
+
if (currentAidaAvailability !== this.aidaAvailability) {
|
551
|
+
this.aidaAvailability = currentAidaAvailability;
|
552
|
+
if (this.aidaAvailability === Host.AidaClient.AidaAccessPreconditions.AVAILABLE) {
|
553
|
+
this.onAiCodeCompletionSettingChanged();
|
554
|
+
} else if (this.aiCodeCompletion) {
|
555
|
+
this.aiCodeCompletion.remove();
|
556
|
+
this.aiCodeCompletion = undefined;
|
557
|
+
}
|
558
|
+
}
|
559
|
+
}
|
560
|
+
|
535
561
|
async onAiCodeCompletionTeaserActionKeyDown(event: Event): Promise<void> {
|
536
562
|
if (this.teaser?.isShowing()) {
|
537
563
|
await this.teaser?.onAction(event);
|
@@ -40,6 +40,7 @@ import * as Platform from '../../core/platform/platform.js';
|
|
40
40
|
import * as Root from '../../core/root/root.js';
|
41
41
|
import * as SDK from '../../core/sdk/sdk.js';
|
42
42
|
import * as Protocol from '../../generated/protocol.js';
|
43
|
+
import * as Badges from '../../models/badges/badges.js';
|
43
44
|
import type * as Elements from '../../models/elements/elements.js';
|
44
45
|
import type * as IssuesManager from '../../models/issues_manager/issues_manager.js';
|
45
46
|
import * as TextUtils from '../../models/text_utils/text_utils.js';
|
@@ -1603,6 +1604,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
1603
1604
|
|
1604
1605
|
if (attributeName !== null && (attributeName.trim() || newText.trim()) && oldText !== newText) {
|
1605
1606
|
this.nodeInternal.setAttribute(attributeName, newText, moveToNextAttributeIfNeeded.bind(this));
|
1607
|
+
Badges.UserBadges.instance().recordAction(Badges.BadgeAction.DOM_ELEMENT_OR_ATTRIBUTE_EDITED);
|
1606
1608
|
return;
|
1607
1609
|
}
|
1608
1610
|
|
@@ -1661,6 +1663,8 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
1661
1663
|
if (!treeOutline) {
|
1662
1664
|
return;
|
1663
1665
|
}
|
1666
|
+
|
1667
|
+
Badges.UserBadges.instance().recordAction(Badges.BadgeAction.DOM_ELEMENT_OR_ATTRIBUTE_EDITED);
|
1664
1668
|
const newTreeItem = treeOutline.selectNodeAfterEdit(wasExpanded, error, newNode);
|
1665
1669
|
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
1666
1670
|
// @ts-expect-error
|
@@ -2581,6 +2585,9 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
2581
2585
|
const adorner = this.adorn(config);
|
2582
2586
|
const onClick = async(): Promise<void> => {
|
2583
2587
|
const {nodeIds} = await node.domModel().agent.invoke_forceShowPopover({nodeId, enable: adorner.isActive()});
|
2588
|
+
if (adorner.isActive()) {
|
2589
|
+
Badges.UserBadges.instance().recordAction(Badges.BadgeAction.MODERN_DOM_BADGE_CLICKED);
|
2590
|
+
}
|
2584
2591
|
for (const closedPopoverNodeId of nodeIds) {
|
2585
2592
|
const node = this.node().domModel().nodeForId(closedPopoverNodeId);
|
2586
2593
|
const treeElement = node && this.treeOutline?.treeElementByNode.get(node);
|
@@ -2617,6 +2624,9 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
2617
2624
|
const onClick = ((() => {
|
2618
2625
|
if (adorner.isActive()) {
|
2619
2626
|
node.domModel().overlayModel().highlightGridInPersistentOverlay(nodeId);
|
2627
|
+
if (isSubgrid) {
|
2628
|
+
Badges.UserBadges.instance().recordAction(Badges.BadgeAction.MODERN_DOM_BADGE_CLICKED);
|
2629
|
+
}
|
2620
2630
|
} else {
|
2621
2631
|
node.domModel().overlayModel().hideGridInPersistentOverlay(nodeId);
|
2622
2632
|
}
|
@@ -2658,6 +2668,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
2658
2668
|
const onClick = ((() => {
|
2659
2669
|
if (adorner.isActive()) {
|
2660
2670
|
node.domModel().overlayModel().highlightGridInPersistentOverlay(nodeId);
|
2671
|
+
Badges.UserBadges.instance().recordAction(Badges.BadgeAction.MODERN_DOM_BADGE_CLICKED);
|
2661
2672
|
} else {
|
2662
2673
|
node.domModel().overlayModel().hideGridInPersistentOverlay(nodeId);
|
2663
2674
|
}
|
@@ -2786,6 +2797,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
2786
2797
|
const model = node.domModel().overlayModel();
|
2787
2798
|
if (adorner.isActive()) {
|
2788
2799
|
model.highlightContainerQueryInPersistentOverlay(nodeId);
|
2800
|
+
Badges.UserBadges.instance().recordAction(Badges.BadgeAction.MODERN_DOM_BADGE_CLICKED);
|
2789
2801
|
} else {
|
2790
2802
|
model.hideContainerQueryInPersistentOverlay(nodeId);
|
2791
2803
|
}
|
@@ -38,6 +38,7 @@ import * as Common from '../../core/common/common.js';
|
|
38
38
|
import * as i18n from '../../core/i18n/i18n.js';
|
39
39
|
import * as Root from '../../core/root/root.js';
|
40
40
|
import * as SDK from '../../core/sdk/sdk.js';
|
41
|
+
import * as Badges from '../../models/badges/badges.js';
|
41
42
|
import * as Elements from '../../models/elements/elements.js';
|
42
43
|
import * as IssuesManager from '../../models/issues_manager/issues_manager.js';
|
43
44
|
import * as CodeHighlighter from '../../ui/components/code_highlighter/code_highlighter.js';
|
@@ -1332,6 +1333,8 @@ export class ElementsTreeOutline extends
|
|
1332
1333
|
return;
|
1333
1334
|
}
|
1334
1335
|
|
1336
|
+
Badges.UserBadges.instance().recordAction(Badges.BadgeAction.DOM_ELEMENT_OR_ATTRIBUTE_EDITED);
|
1337
|
+
|
1335
1338
|
// Select it and expand if necessary. We force tree update so that it processes dom events and is up to date.
|
1336
1339
|
this.runPendingUpdates();
|
1337
1340
|
|
@@ -41,6 +41,7 @@ import * as Platform from '../../core/platform/platform.js';
|
|
41
41
|
import * as Root from '../../core/root/root.js';
|
42
42
|
import * as SDK from '../../core/sdk/sdk.js';
|
43
43
|
import * as Protocol from '../../generated/protocol.js';
|
44
|
+
import * as Badges from '../../models/badges/badges.js';
|
44
45
|
import * as Bindings from '../../models/bindings/bindings.js';
|
45
46
|
import * as TextUtils from '../../models/text_utils/text_utils.js';
|
46
47
|
import * as Buttons from '../../ui/components/buttons/buttons.js';
|
@@ -1546,6 +1547,8 @@ export class StylePropertiesSection {
|
|
1546
1547
|
if (!success) {
|
1547
1548
|
return Promise.resolve();
|
1548
1549
|
}
|
1550
|
+
|
1551
|
+
Badges.UserBadges.instance().recordAction(Badges.BadgeAction.CSS_RULE_MODIFIED);
|
1549
1552
|
return this.matchedStyles.recomputeMatchingSelectors(rule).then(updateSourceRanges.bind(this, rule));
|
1550
1553
|
}
|
1551
1554
|
|
@@ -10,6 +10,7 @@ import * as i18n from '../../core/i18n/i18n.js';
|
|
10
10
|
import * as Platform from '../../core/platform/platform.js';
|
11
11
|
import * as Root from '../../core/root/root.js';
|
12
12
|
import * as SDK from '../../core/sdk/sdk.js';
|
13
|
+
import * as Badges from '../../models/badges/badges.js';
|
13
14
|
import * as Bindings from '../../models/bindings/bindings.js';
|
14
15
|
import * as TextUtils from '../../models/text_utils/text_utils.js';
|
15
16
|
import type * as CodeMirror from '../../third_party/codemirror.next/codemirror.next.js';
|
@@ -3292,6 +3293,10 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
|
|
3292
3293
|
|
3293
3294
|
const overwriteProperty = !this.newProperty || hasBeenEditedIncrementally;
|
3294
3295
|
let success: boolean = await this.property.setText(styleText, majorChange, overwriteProperty);
|
3296
|
+
if (success && majorChange) {
|
3297
|
+
Badges.UserBadges.instance().recordAction(Badges.BadgeAction.CSS_RULE_MODIFIED);
|
3298
|
+
}
|
3299
|
+
|
3295
3300
|
// Revert to the original text if applying the new text failed
|
3296
3301
|
if (hasBeenEditedIncrementally && majorChange && !success) {
|
3297
3302
|
majorChange = false;
|
@@ -336,19 +336,13 @@ export class GenericSettingsTab extends UI.Widget.VBox implements SettingsTab {
|
|
336
336
|
this.#updateSyncSectionTimerId = -1;
|
337
337
|
}
|
338
338
|
|
339
|
-
void Promise
|
340
|
-
.
|
341
|
-
|
342
|
-
resolve => Host.InspectorFrontendHost.InspectorFrontendHostInstance.getSyncInformation(resolve)),
|
343
|
-
Root.Runtime.hostConfig.devToolsGdpProfiles?.enabled ? Host.GdpClient.GdpClient.instance().getProfile() :
|
344
|
-
Promise.resolve(undefined),
|
345
|
-
])
|
346
|
-
.then(([syncInfo, gdpProfile]) => {
|
339
|
+
void new Promise<Host.InspectorFrontendHostAPI.SyncInformation>(
|
340
|
+
resolve => Host.InspectorFrontendHost.InspectorFrontendHostInstance.getSyncInformation(resolve))
|
341
|
+
.then(syncInfo => {
|
347
342
|
this.syncSection.data = {
|
348
343
|
syncInfo,
|
349
344
|
syncSetting: Common.Settings.moduleSetting('sync-preferences') as Common.Settings.Setting<boolean>,
|
350
345
|
receiveBadgesSetting: Common.Settings.Settings.instance().moduleSetting('receive-gdp-badges'),
|
351
|
-
gdpProfile: gdpProfile ?? undefined,
|
352
346
|
};
|
353
347
|
if (!syncInfo.isSyncActive || !syncInfo.arePreferencesSynced) {
|
354
348
|
this.#updateSyncSectionTimerId = window.setTimeout(this.updateSyncSection.bind(this), 500);
|
@@ -117,7 +117,6 @@ export interface SyncSectionData {
|
|
117
117
|
syncInfo: Host.InspectorFrontendHostAPI.SyncInformation;
|
118
118
|
syncSetting: Common.Settings.Setting<boolean>;
|
119
119
|
receiveBadgesSetting: Common.Settings.Setting<boolean>;
|
120
|
-
gdpProfile?: Host.GdpClient.Profile;
|
121
120
|
}
|
122
121
|
|
123
122
|
export class SyncSection extends HTMLElement {
|
@@ -132,7 +131,7 @@ export class SyncSection extends HTMLElement {
|
|
132
131
|
this.#syncInfo = data.syncInfo;
|
133
132
|
this.#syncSetting = data.syncSetting;
|
134
133
|
this.#receiveBadgesSetting = data.receiveBadgesSetting;
|
135
|
-
this.#
|
134
|
+
void this.#updateGdpProfile();
|
136
135
|
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
|
137
136
|
}
|
138
137
|
|
@@ -157,6 +156,11 @@ export class SyncSection extends HTMLElement {
|
|
157
156
|
`, this.#shadow, {host: this});
|
158
157
|
// clang-format on
|
159
158
|
}
|
159
|
+
|
160
|
+
async #updateGdpProfile(): Promise<void> {
|
161
|
+
this.#gdpProfile = await Host.GdpClient.GdpClient.instance().getProfile() ?? undefined;
|
162
|
+
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
|
163
|
+
}
|
160
164
|
}
|
161
165
|
|
162
166
|
function renderSettingCheckboxIfNeeded(
|
@@ -22,6 +22,8 @@ const CITATIONS_TOOLTIP_ID = 'sources-ai-code-completion-citations-tooltip';
|
|
22
22
|
|
23
23
|
export class AiCodeCompletionPlugin extends Plugin {
|
24
24
|
#aidaClient?: Host.AidaClient.AidaClient;
|
25
|
+
#aidaAvailability?: Host.AidaClient.AidaAccessPreconditions;
|
26
|
+
#boundOnAidaAvailabilityChange: () => Promise<void>;
|
25
27
|
#aiCodeCompletion?: AiCodeCompletion.AiCodeCompletion.AiCodeCompletion;
|
26
28
|
#aiCodeCompletionSetting = Common.Settings.Settings.instance().createSetting('ai-code-completion-enabled', false);
|
27
29
|
#aiCodeCompletionTeaserDismissedSetting =
|
@@ -48,6 +50,10 @@ export class AiCodeCompletionPlugin extends Plugin {
|
|
48
50
|
}
|
49
51
|
this.#boundEditorKeyDown = this.#editorKeyDown.bind(this);
|
50
52
|
this.#boundOnAiCodeCompletionSettingChanged = this.#onAiCodeCompletionSettingChanged.bind(this);
|
53
|
+
this.#boundOnAidaAvailabilityChange = this.#onAidaAvailabilityChange.bind(this);
|
54
|
+
Host.AidaClient.HostConfigTracker.instance().addEventListener(
|
55
|
+
Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnAidaAvailabilityChange);
|
56
|
+
void this.#onAidaAvailabilityChange();
|
51
57
|
const showTeaser = !this.#aiCodeCompletionSetting.get() && !this.#aiCodeCompletionTeaserDismissedSetting.get();
|
52
58
|
if (showTeaser) {
|
53
59
|
this.#teaser = new PanelCommon.AiCodeCompletionTeaser({onDetach: this.#detachAiCodeCompletionTeaser.bind(this)});
|
@@ -61,6 +67,8 @@ export class AiCodeCompletionPlugin extends Plugin {
|
|
61
67
|
override dispose(): void {
|
62
68
|
this.#teaser = undefined;
|
63
69
|
this.#aiCodeCompletionSetting.removeChangeListener(this.#boundOnAiCodeCompletionSettingChanged);
|
70
|
+
Host.AidaClient.HostConfigTracker.instance().removeEventListener(
|
71
|
+
Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnAidaAvailabilityChange);
|
64
72
|
this.#editor?.removeEventListener('keydown', this.#boundEditorKeyDown);
|
65
73
|
this.#cleanupAiCodeCompletion();
|
66
74
|
super.dispose();
|
@@ -203,23 +211,31 @@ export class AiCodeCompletionPlugin extends Plugin {
|
|
203
211
|
this.#detachAiCodeCompletionTeaser();
|
204
212
|
this.#teaser = undefined;
|
205
213
|
}
|
206
|
-
this.#aiCodeCompletion
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
214
|
+
if (!this.#aiCodeCompletion) {
|
215
|
+
this.#aiCodeCompletion = new AiCodeCompletion.AiCodeCompletion.AiCodeCompletion(
|
216
|
+
{aidaClient: this.#aidaClient}, this.#editor, AiCodeCompletion.AiCodeCompletion.Panel.SOURCES);
|
217
|
+
this.#aiCodeCompletion.addEventListener(
|
218
|
+
AiCodeCompletion.AiCodeCompletion.Events.REQUEST_TRIGGERED, this.#onAiRequestTriggered, this);
|
219
|
+
this.#aiCodeCompletion.addEventListener(
|
220
|
+
AiCodeCompletion.AiCodeCompletion.Events.RESPONSE_RECEIVED, this.#onAiResponseReceived, this);
|
221
|
+
}
|
212
222
|
this.#createAiCodeCompletionDisclaimer();
|
213
223
|
this.#createAiCodeCompletionCitationsToolbar();
|
214
224
|
}
|
215
225
|
|
216
226
|
#createAiCodeCompletionDisclaimer(): void {
|
227
|
+
if (this.#aiCodeCompletionDisclaimer) {
|
228
|
+
return;
|
229
|
+
}
|
217
230
|
this.#aiCodeCompletionDisclaimer = new PanelCommon.AiCodeCompletionDisclaimer();
|
218
231
|
this.#aiCodeCompletionDisclaimer.disclaimerTooltipId = DISCLAIMER_TOOLTIP_ID;
|
219
232
|
this.#aiCodeCompletionDisclaimer.show(this.#aiCodeCompletionDisclaimerContainer, undefined, true);
|
220
233
|
}
|
221
234
|
|
222
235
|
#createAiCodeCompletionCitationsToolbar(): void {
|
236
|
+
if (this.#aiCodeCompletionCitationsToolbar) {
|
237
|
+
return;
|
238
|
+
}
|
223
239
|
this.#aiCodeCompletionCitationsToolbar =
|
224
240
|
new PanelCommon.AiCodeCompletionSummaryToolbar({citationsTooltipId: CITATIONS_TOOLTIP_ID, hasTopBorder: true});
|
225
241
|
this.#aiCodeCompletionCitationsToolbar.show(this.#aiCodeCompletionCitationsToolbarContainer, undefined, true);
|
@@ -253,12 +269,25 @@ export class AiCodeCompletionPlugin extends Plugin {
|
|
253
269
|
}
|
254
270
|
}
|
255
271
|
|
272
|
+
async #onAidaAvailabilityChange(): Promise<void> {
|
273
|
+
const currentAidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
|
274
|
+
if (currentAidaAvailability !== this.#aidaAvailability) {
|
275
|
+
this.#aidaAvailability = currentAidaAvailability;
|
276
|
+
if (this.#aidaAvailability === Host.AidaClient.AidaAccessPreconditions.AVAILABLE) {
|
277
|
+
this.#onAiCodeCompletionSettingChanged();
|
278
|
+
} else if (this.#aiCodeCompletion) {
|
279
|
+
this.#cleanupAiCodeCompletion();
|
280
|
+
}
|
281
|
+
}
|
282
|
+
}
|
283
|
+
|
256
284
|
#cleanupAiCodeCompletion(): void {
|
257
285
|
this.#aiCodeCompletion?.removeEventListener(
|
258
286
|
AiCodeCompletion.AiCodeCompletion.Events.REQUEST_TRIGGERED, this.#onAiRequestTriggered, this);
|
259
287
|
this.#aiCodeCompletion?.removeEventListener(
|
260
288
|
AiCodeCompletion.AiCodeCompletion.Events.RESPONSE_RECEIVED, this.#onAiResponseReceived, this);
|
261
289
|
this.#aiCodeCompletion?.remove();
|
290
|
+
this.#aiCodeCompletion = undefined;
|
262
291
|
this.#aiCodeCompletionCitations = [];
|
263
292
|
this.#aiCodeCompletionDisclaimerContainer.removeChildren();
|
264
293
|
this.#aiCodeCompletionDisclaimer = undefined;
|
@@ -1534,7 +1534,7 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
1534
1534
|
// extensions sourcemaps provide little to no-value for the exported trace
|
1535
1535
|
// debugging, so they are filtered out.
|
1536
1536
|
return metadata.sourceMaps.filter(value => {
|
1537
|
-
return
|
1537
|
+
return !Trace.Helpers.Trace.isExtensionUrl(value.url);
|
1538
1538
|
});
|
1539
1539
|
}
|
1540
1540
|
|
@@ -2733,14 +2733,27 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
2733
2733
|
async #executeNewTrace(
|
2734
2734
|
collectedEvents: Trace.Types.Events.Event[], isFreshRecording: boolean,
|
2735
2735
|
metadata: Trace.Types.File.MetaData|null): Promise<void> {
|
2736
|
-
|
2737
|
-
|
2738
|
-
|
2739
|
-
|
2740
|
-
|
2741
|
-
|
2736
|
+
const config: Trace.Types.Configuration.ParseOptions = {
|
2737
|
+
metadata: metadata ?? undefined,
|
2738
|
+
isFreshRecording,
|
2739
|
+
resolveSourceMap: this.#createSourceMapResolver(isFreshRecording, metadata),
|
2740
|
+
};
|
2741
|
+
|
2742
|
+
if (window.location.href.includes('devtools/bundled') || window.location.search.includes('debugFrontend')) {
|
2743
|
+
// Someone is debugging DevTools, enable the logger to give timings
|
2744
|
+
// when tracing the performance panel itself.
|
2745
|
+
const times: Record<string, number> = {};
|
2746
|
+
config.logger = {
|
2747
|
+
start(id) {
|
2748
|
+
times[id] = performance.now();
|
2742
2749
|
},
|
2743
|
-
|
2750
|
+
end(id) {
|
2751
|
+
performance.measure(id, {start: times[id]});
|
2752
|
+
},
|
2753
|
+
};
|
2754
|
+
}
|
2755
|
+
|
2756
|
+
await this.#traceEngineModel.parse(collectedEvents, config);
|
2744
2757
|
|
2745
2758
|
// Store all source maps on the trace metadata.
|
2746
2759
|
// If not fresh, we can't validate the maps are still accurate.
|
@@ -3039,8 +3052,7 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
3039
3052
|
for (const modelName in insightsForNav.model) {
|
3040
3053
|
const model = modelName as keyof Trace.Insights.Types.InsightModelsType;
|
3041
3054
|
const insight = insightsForNav.model[model];
|
3042
|
-
const formatter = new AiAssistanceModel.PerformanceInsightFormatter(
|
3043
|
-
AiAssistanceModel.PERF_AGENT_UNIT_FORMATTERS, parsedTrace, insight);
|
3055
|
+
const formatter = new AiAssistanceModel.PerformanceInsightFormatter(parsedTrace, insight);
|
3044
3056
|
if (!formatter.insightIsSupported()) {
|
3045
3057
|
// Not all Insights are integrated with "Ask AI" yet, let's avoid
|
3046
3058
|
// filling up the response with those ones because there will be no
|
@@ -476,6 +476,9 @@ const UIStrings = {
|
|
476
476
|
const str_ = i18n.i18n.registerUIStrings('panels/timeline/TimelineUIUtils.ts', UIStrings);
|
477
477
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
478
478
|
|
479
|
+
// Look for scheme:// plus text and exclude any punctuation at the end.
|
480
|
+
export const URL_REGEX = /(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\/\/)[^\s"]{2,}[^\s"'\)\}\],:;.!?]/u;
|
481
|
+
|
479
482
|
let eventDispatchDesciptors: EventDispatchTypeDescriptor[];
|
480
483
|
|
481
484
|
let colorGenerator: Common.Color.Generator;
|
@@ -897,9 +900,7 @@ export class TimelineUIUtils {
|
|
897
900
|
* of the link is the URL, so the visible string to the user is unchanged.
|
898
901
|
*/
|
899
902
|
static parseStringForLinks(rawString: string): DocumentFragment {
|
900
|
-
|
901
|
-
const urlRegex = /(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\/\/)[^\s"]{2,}[^\s"'\)\}\],:;.!?]/u;
|
902
|
-
const results = TextUtils.TextUtils.Utils.splitStringByRegexes(rawString, [urlRegex]);
|
903
|
+
const results = TextUtils.TextUtils.Utils.splitStringByRegexes(rawString, [URL_REGEX]);
|
903
904
|
const nodes = results.map(result => {
|
904
905
|
if (result.regexIndex === -1) {
|
905
906
|
return result.value;
|
@@ -126,22 +126,3 @@ export class AIQueries {
|
|
126
126
|
.filter(tree => !!tree);
|
127
127
|
}
|
128
128
|
}
|
129
|
-
|
130
|
-
/**
|
131
|
-
* Calculates the trace bounds for the given insight that are relevant.
|
132
|
-
*
|
133
|
-
* Uses the insight's overlays to determine the relevant trace bounds. If there are
|
134
|
-
* no overlays, falls back to the insight set's navigation bounds.
|
135
|
-
*/
|
136
|
-
export function insightBounds(
|
137
|
-
insight: Trace.Insights.Types.InsightModel,
|
138
|
-
insightSetBounds: Trace.Types.Timing.TraceWindowMicro): Trace.Types.Timing.TraceWindowMicro {
|
139
|
-
const overlays = insight.createOverlays?.() ?? [];
|
140
|
-
const windows = overlays.map(Trace.Helpers.Timing.traceWindowFromOverlay).filter(bounds => !!bounds);
|
141
|
-
const overlaysBounds = Trace.Helpers.Timing.combineTraceWindowsMicro(windows);
|
142
|
-
if (overlaysBounds) {
|
143
|
-
return overlaysBounds;
|
144
|
-
}
|
145
|
-
|
146
|
-
return insightSetBounds;
|
147
|
-
}
|
package/package.json
CHANGED
@@ -1,9 +0,0 @@
|
|
1
|
-
// Copyright 2025 The Chromium Authors
|
2
|
-
// Use of this source code is governed by a BSD-style license that can be
|
3
|
-
// found in the LICENSE file.
|
4
|
-
|
5
|
-
export interface UnitFormatters {
|
6
|
-
millis: (x: number) => string;
|
7
|
-
micros: (x: number) => string;
|
8
|
-
bytes: (x: number) => string;
|
9
|
-
}
|