chrome-devtools-frontend 1.0.1553956 → 1.0.1555430
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/spark.svg +10 -0
- package/front_end/core/protocol_client/InspectorBackend.ts +1 -1
- package/front_end/core/root/Runtime.ts +0 -4
- package/front_end/core/sdk/DOMModel.ts +101 -7
- package/front_end/core/sdk/ResourceTreeModel.ts +0 -1
- package/front_end/generated/SupportedCSSProperties.js +18 -0
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +1 -1
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +1 -1
- package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +1 -1
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +1 -1
- package/front_end/{ui/components → models}/annotations/AnnotationRepository.ts +3 -3
- package/front_end/models/annotations/README.md +7 -0
- package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +8 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +15 -23
- package/front_end/models/stack_trace/StackTrace.ts +13 -2
- package/front_end/models/stack_trace/StackTraceImpl.ts +81 -6
- package/front_end/models/stack_trace/StackTraceModel.ts +35 -3
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +45 -4
- package/front_end/panels/ai_assistance/components/ArtifactsViewer.ts +57 -0
- package/front_end/panels/ai_assistance/components/ChatView.ts +1 -0
- package/front_end/panels/ai_assistance/components/artifactsViewer.css +10 -0
- package/front_end/panels/application/BounceTrackingMitigationsTreeElement.ts +2 -6
- package/front_end/panels/application/components/BounceTrackingMitigationsView.ts +133 -118
- package/front_end/panels/application/preloading/PreloadingView.ts +12 -6
- package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +230 -237
- package/front_end/panels/application/preloading/components/PreloadingGrid.ts +96 -79
- package/front_end/panels/application/preloading/components/preloadingGrid.css +26 -29
- package/front_end/panels/application/preloading/preloadingView.css +6 -0
- package/front_end/panels/common/Annotation.ts +1 -1
- package/front_end/panels/common/AnnotationManager.ts +1 -1
- package/front_end/panels/common/ExtensionView.ts +1 -0
- package/front_end/panels/console/ConsoleContextSelector.ts +74 -9
- package/front_end/panels/console/consoleContextSelector.css +31 -29
- package/front_end/panels/coverage/coverageListView.css +59 -57
- package/front_end/panels/elements/ElementsPanel.ts +1 -1
- package/front_end/panels/elements/ElementsTreeElement.ts +39 -1
- package/front_end/panels/elements/ElementsTreeOutline.ts +23 -21
- package/front_end/panels/elements/TopLayerContainer.ts +26 -91
- package/front_end/panels/explain/components/ConsoleInsight.ts +3 -3
- package/front_end/panels/network/NetworkItemView.ts +1 -1
- package/front_end/panels/network/NetworkLogView.ts +1 -1
- package/front_end/panels/network/NetworkPanel.ts +1 -1
- package/front_end/panels/recorder/RecorderController.ts +0 -1
- package/front_end/panels/security/CookieControlsView.ts +21 -10
- package/front_end/panels/security/SecurityPanelSidebar.ts +5 -0
- package/front_end/panels/timeline/CompatibilityTracksAppender.ts +0 -1
- package/front_end/panels/timeline/TimelineUIUtils.ts +5 -8
- package/front_end/panels/timeline/components/Sidebar.ts +16 -7
- package/front_end/panels/timeline/components/SidebarInsightsTab.ts +169 -129
- package/front_end/panels/timeline/components/TimelineSummary.ts +75 -54
- package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +16 -25
- package/front_end/panels/timeline/components/insights/Cache.ts +12 -8
- package/front_end/panels/timeline/components/insights/Checklist.ts +53 -43
- package/front_end/panels/timeline/components/insights/DOMSize.ts +25 -21
- package/front_end/panels/timeline/components/insights/DocumentLatency.ts +6 -3
- package/front_end/panels/timeline/components/insights/DuplicatedJavaScript.ts +7 -7
- package/front_end/panels/timeline/components/insights/FontDisplay.ts +7 -5
- package/front_end/panels/timeline/components/insights/ForcedReflow.ts +11 -9
- package/front_end/panels/timeline/components/insights/INPBreakdown.ts +7 -6
- package/front_end/panels/timeline/components/insights/ImageDelivery.ts +7 -5
- package/front_end/panels/timeline/components/insights/InsightRenderer.ts +20 -18
- package/front_end/panels/timeline/components/insights/LCPBreakdown.ts +12 -12
- package/front_end/panels/timeline/components/insights/LCPDiscovery.ts +7 -3
- package/front_end/panels/timeline/components/insights/LegacyJavaScript.ts +7 -7
- package/front_end/panels/timeline/components/insights/ModernHTTP.ts +7 -5
- package/front_end/panels/timeline/components/insights/NetworkDependencyTree.ts +15 -13
- package/front_end/panels/timeline/components/insights/RenderBlocking.ts +2 -2
- package/front_end/panels/timeline/components/insights/SlowCSSSelector.ts +15 -14
- package/front_end/panels/timeline/components/insights/Table.ts +152 -130
- package/front_end/panels/timeline/components/insights/ThirdParties.ts +11 -9
- package/front_end/panels/timeline/components/sidebarInsightsTab.css +50 -48
- package/front_end/panels/timeline/components/timelineSummary.css +58 -57
- package/front_end/panels/timeline/thirdPartyTreeView.css +109 -0
- package/front_end/panels/timeline/timelineDetailsView.css +2 -4
- package/front_end/panels/timeline/timelinePanel.css +0 -110
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/settings/SettingCheckbox.ts +4 -6
- package/front_end/ui/legacy/TabbedPane.ts +20 -13
- package/front_end/ui/legacy/ViewManager.ts +2 -32
- package/front_end/ui/legacy/Widget.ts +1 -3
- package/front_end/ui/legacy/tabbedPane.css +4 -7
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +1 -1
- /package/front_end/{ui/components → models}/annotations/AnnotationType.ts +0 -0
- /package/front_end/{ui/components → models}/annotations/annotations.ts +0 -0
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
// Copyright 2024 The Chromium Authors
|
|
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
|
-
/* eslint-disable @devtools/no-lit-render-outside-of-view */
|
|
5
4
|
|
|
6
5
|
import * as Trace from '../../../models/trace/trace.js';
|
|
7
6
|
import * as Buttons from '../../../ui/components/buttons/buttons.js';
|
|
8
|
-
import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
|
|
9
7
|
import * as UI from '../../../ui/legacy/legacy.js';
|
|
10
8
|
import * as Lit from '../../../ui/lit/lit.js';
|
|
11
9
|
import * as Utils from '../utils/utils.js';
|
|
@@ -16,9 +14,136 @@ import sidebarInsightsTabStyles from './sidebarInsightsTab.css.js';
|
|
|
16
14
|
import {SidebarSingleInsightSet, type SidebarSingleInsightSetData} from './SidebarSingleInsightSet.js';
|
|
17
15
|
|
|
18
16
|
const {html} = Lit;
|
|
17
|
+
const {widgetConfig} = UI.Widget;
|
|
18
|
+
|
|
19
|
+
interface ViewInput {
|
|
20
|
+
parsedTrace: Trace.TraceModel.ParsedTrace;
|
|
21
|
+
labels: string[];
|
|
22
|
+
activeInsightSet: Trace.Insights.Types.InsightSet|null;
|
|
23
|
+
activeInsight: ActiveInsight|null;
|
|
24
|
+
selectedCategory: Trace.Insights.Types.InsightCategory;
|
|
25
|
+
onInsightSetToggled: (insightSet: Trace.Insights.Types.InsightSet) => void;
|
|
26
|
+
onInsightSetHovered: (insightSet: Trace.Insights.Types.InsightSet) => void;
|
|
27
|
+
onInsightSetUnhovered: () => void;
|
|
28
|
+
onZoomClick: (insightSet: Trace.Insights.Types.InsightSet) => void;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;
|
|
32
|
+
|
|
33
|
+
export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
34
|
+
const {
|
|
35
|
+
parsedTrace,
|
|
36
|
+
labels,
|
|
37
|
+
activeInsightSet,
|
|
38
|
+
activeInsight,
|
|
39
|
+
selectedCategory,
|
|
40
|
+
onInsightSetToggled,
|
|
41
|
+
onInsightSetHovered,
|
|
42
|
+
onInsightSetUnhovered,
|
|
43
|
+
onZoomClick,
|
|
44
|
+
} = input;
|
|
45
|
+
|
|
46
|
+
const insights = parsedTrace.insights;
|
|
47
|
+
if (!insights) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
19
50
|
|
|
20
|
-
|
|
21
|
-
|
|
51
|
+
const hasMultipleInsightSets = insights.size > 1;
|
|
52
|
+
|
|
53
|
+
// clang-format off
|
|
54
|
+
Lit.render(html`
|
|
55
|
+
<style>${sidebarInsightsTabStyles}</style>
|
|
56
|
+
<div class="insight-sets-wrapper">
|
|
57
|
+
${[...insights.values()].map((insightSet, index) => {
|
|
58
|
+
const {id, url} = insightSet;
|
|
59
|
+
const data: SidebarSingleInsightSetData = {
|
|
60
|
+
insightSetKey: id,
|
|
61
|
+
activeCategory: selectedCategory,
|
|
62
|
+
activeInsight,
|
|
63
|
+
parsedTrace,
|
|
64
|
+
};
|
|
65
|
+
const selected = insightSet === activeInsightSet;
|
|
66
|
+
|
|
67
|
+
const contents = html`
|
|
68
|
+
<devtools-widget
|
|
69
|
+
data-insight-set-key=${id}
|
|
70
|
+
.widgetConfig=${widgetConfig(SidebarSingleInsightSet, {data})}
|
|
71
|
+
></devtools-widget>
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
if (hasMultipleInsightSets) {
|
|
75
|
+
return html`<details ?open=${selected}>
|
|
76
|
+
<summary
|
|
77
|
+
@click=${() => onInsightSetToggled(insightSet)}
|
|
78
|
+
@mouseenter=${() => onInsightSetHovered(insightSet)}
|
|
79
|
+
@mouseleave=${() => onInsightSetUnhovered()}
|
|
80
|
+
title=${url.href}>
|
|
81
|
+
${renderDropdownIcon(selected)}
|
|
82
|
+
<span>${labels[index]}</span>
|
|
83
|
+
<span class='zoom-button'
|
|
84
|
+
@click=${(event: Event) => {
|
|
85
|
+
event.stopPropagation();
|
|
86
|
+
onZoomClick(insightSet);
|
|
87
|
+
}}
|
|
88
|
+
>
|
|
89
|
+
${renderZoomButton(selected)}
|
|
90
|
+
</span>
|
|
91
|
+
</summary>
|
|
92
|
+
${contents}
|
|
93
|
+
</details>`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return contents;
|
|
97
|
+
})}
|
|
98
|
+
</div>
|
|
99
|
+
`, target);
|
|
100
|
+
// clang-format on
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
function renderZoomButton(insightSetToggled: boolean): Lit.TemplateResult {
|
|
104
|
+
const classes = Lit.Directives.classMap({
|
|
105
|
+
'zoom-icon': true,
|
|
106
|
+
active: insightSetToggled,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// clang-format off
|
|
110
|
+
return html`
|
|
111
|
+
<div class=${classes}>
|
|
112
|
+
<devtools-button .data=${{
|
|
113
|
+
variant: Buttons.Button.Variant.ICON,
|
|
114
|
+
iconName: 'center-focus-weak',
|
|
115
|
+
size: Buttons.Button.Size.SMALL,
|
|
116
|
+
} as Buttons.Button.ButtonData}
|
|
117
|
+
></devtools-button></div>`;
|
|
118
|
+
// clang-format on
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function renderDropdownIcon(insightSetToggled: boolean): Lit.TemplateResult {
|
|
122
|
+
const containerClasses = Lit.Directives.classMap({
|
|
123
|
+
'dropdown-icon': true,
|
|
124
|
+
active: insightSetToggled,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// clang-format off
|
|
128
|
+
return html`
|
|
129
|
+
<div class=${containerClasses}>
|
|
130
|
+
<devtools-button .data=${{
|
|
131
|
+
variant: Buttons.Button.Variant.ICON,
|
|
132
|
+
iconName: 'chevron-right',
|
|
133
|
+
size: Buttons.Button.Size.SMALL,
|
|
134
|
+
} as Buttons.Button.ButtonData}
|
|
135
|
+
></devtools-button></div>
|
|
136
|
+
`;
|
|
137
|
+
// clang-format on
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export class SidebarInsightsTab extends UI.Widget.Widget {
|
|
141
|
+
static createWidgetElement(): UI.Widget.WidgetElement<SidebarInsightsTab> {
|
|
142
|
+
const widgetElement = document.createElement('devtools-widget') as UI.Widget.WidgetElement<SidebarInsightsTab>;
|
|
143
|
+
return widgetElement;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
#view: View;
|
|
22
147
|
#parsedTrace: Trace.TraceModel.ParsedTrace|null = null;
|
|
23
148
|
#activeInsight: ActiveInsight|null = null;
|
|
24
149
|
#selectedCategory = Trace.Insights.Types.InsightCategory.ALL;
|
|
@@ -26,9 +151,14 @@ export class SidebarInsightsTab extends HTMLElement {
|
|
|
26
151
|
* When a trace has sets of insights, we show an accordion with each
|
|
27
152
|
* set within. A set can be specific to a single navigation, or include the
|
|
28
153
|
* beginning of the trace up to the first navigation.
|
|
29
|
-
* You can only have one of these open at any time
|
|
154
|
+
* You can only have one of these open at any time.
|
|
30
155
|
*/
|
|
31
|
-
#
|
|
156
|
+
#selectedInsightSet: Trace.Insights.Types.InsightSet|null = null;
|
|
157
|
+
|
|
158
|
+
constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
|
|
159
|
+
super(element, {useShadowDom: true});
|
|
160
|
+
this.#view = view;
|
|
161
|
+
}
|
|
32
162
|
|
|
33
163
|
// TODO(paulirish): add back a disconnectedCallback() to avoid memory leaks that doesn't cause b/372943062
|
|
34
164
|
|
|
@@ -38,14 +168,14 @@ export class SidebarInsightsTab extends HTMLElement {
|
|
|
38
168
|
}
|
|
39
169
|
|
|
40
170
|
this.#parsedTrace = data;
|
|
41
|
-
this.#
|
|
171
|
+
this.#selectedInsightSet = null;
|
|
42
172
|
|
|
43
173
|
if (this.#parsedTrace?.insights) {
|
|
44
174
|
/** Select the first set. Filtering out trivial sets was done back in {@link Trace.Processor.#computeInsightsForInitialTracePeriod} */
|
|
45
|
-
this.#
|
|
175
|
+
this.#selectedInsightSet = [...this.#parsedTrace.insights.values()].at(0) ?? null;
|
|
46
176
|
}
|
|
47
177
|
|
|
48
|
-
|
|
178
|
+
this.requestUpdate();
|
|
49
179
|
}
|
|
50
180
|
|
|
51
181
|
get activeInsight(): ActiveInsight|null {
|
|
@@ -56,81 +186,39 @@ export class SidebarInsightsTab extends HTMLElement {
|
|
|
56
186
|
if (active === this.#activeInsight) {
|
|
57
187
|
return;
|
|
58
188
|
}
|
|
189
|
+
|
|
59
190
|
this.#activeInsight = active;
|
|
60
191
|
|
|
61
|
-
// Only update
|
|
192
|
+
// Only update selectedInsightSet if there is an active insight. Otherwise, closing an insight
|
|
62
193
|
// would also collapse the insight set. Usually the proper insight set is already set because
|
|
63
194
|
// the user has it open already in order for this setter to be called, but insights can also
|
|
64
195
|
// be activated by clicking on a insight chip in the Summary panel, which may require opening
|
|
65
196
|
// a different insight set.
|
|
66
197
|
if (this.#activeInsight) {
|
|
67
|
-
this.#
|
|
198
|
+
this.#selectedInsightSet = this.#parsedTrace?.insights?.get(this.#activeInsight.insightSetKey) ?? null;
|
|
68
199
|
}
|
|
69
|
-
|
|
200
|
+
this.requestUpdate();
|
|
70
201
|
}
|
|
71
202
|
|
|
72
|
-
#
|
|
73
|
-
this.#
|
|
203
|
+
#onInsightSetToggled(insightSet: Trace.Insights.Types.InsightSet): void {
|
|
204
|
+
this.#selectedInsightSet = this.#selectedInsightSet === insightSet ? null : insightSet;
|
|
74
205
|
// Update the active insight set.
|
|
75
|
-
if (this.#
|
|
76
|
-
this.dispatchEvent(new Insights.SidebarInsight.InsightDeactivated());
|
|
206
|
+
if (this.#selectedInsightSet?.id !== this.#activeInsight?.insightSetKey) {
|
|
207
|
+
this.element.dispatchEvent(new Insights.SidebarInsight.InsightDeactivated());
|
|
77
208
|
}
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
#insightSetHovered(id: string): void {
|
|
82
|
-
const data = this.#parsedTrace?.insights?.get(id);
|
|
83
|
-
data && this.dispatchEvent(new Insights.SidebarInsight.InsightSetHovered(data.bounds));
|
|
209
|
+
this.requestUpdate();
|
|
84
210
|
}
|
|
85
211
|
|
|
86
|
-
#
|
|
87
|
-
this.dispatchEvent(new Insights.SidebarInsight.InsightSetHovered());
|
|
212
|
+
#onInsightSetHovered(insightSet: Trace.Insights.Types.InsightSet): void {
|
|
213
|
+
this.element.dispatchEvent(new Insights.SidebarInsight.InsightSetHovered(insightSet.bounds));
|
|
88
214
|
}
|
|
89
215
|
|
|
90
|
-
#
|
|
91
|
-
|
|
92
|
-
const data = this.#parsedTrace?.insights?.get(id);
|
|
93
|
-
if (!data) {
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
this.dispatchEvent(new Insights.SidebarInsight.InsightSetZoom(data.bounds));
|
|
216
|
+
#onInsightSetUnhovered(): void {
|
|
217
|
+
this.element.dispatchEvent(new Insights.SidebarInsight.InsightSetHovered());
|
|
97
218
|
}
|
|
98
219
|
|
|
99
|
-
#
|
|
100
|
-
|
|
101
|
-
'zoom-icon': true,
|
|
102
|
-
active: insightSetToggled,
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
// clang-format off
|
|
106
|
-
return html`
|
|
107
|
-
<div class=${classes}>
|
|
108
|
-
<devtools-button .data=${{
|
|
109
|
-
variant: Buttons.Button.Variant.ICON,
|
|
110
|
-
iconName: 'center-focus-weak',
|
|
111
|
-
size: Buttons.Button.Size.SMALL,
|
|
112
|
-
} as Buttons.Button.ButtonData}
|
|
113
|
-
></devtools-button></div>`;
|
|
114
|
-
// clang-format on
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
#renderDropdownIcon(insightSetToggled: boolean): Lit.TemplateResult {
|
|
118
|
-
const containerClasses = Lit.Directives.classMap({
|
|
119
|
-
'dropdown-icon': true,
|
|
120
|
-
active: insightSetToggled,
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
// clang-format off
|
|
124
|
-
return html`
|
|
125
|
-
<div class=${containerClasses}>
|
|
126
|
-
<devtools-button .data=${{
|
|
127
|
-
variant: Buttons.Button.Variant.ICON,
|
|
128
|
-
iconName: 'chevron-right',
|
|
129
|
-
size: Buttons.Button.Size.SMALL,
|
|
130
|
-
} as Buttons.Button.ButtonData}
|
|
131
|
-
></devtools-button></div>
|
|
132
|
-
`;
|
|
133
|
-
// clang-format on
|
|
220
|
+
#onZoomClick(insightSet: Trace.Insights.Types.InsightSet): void {
|
|
221
|
+
this.element.dispatchEvent(new Insights.SidebarInsight.InsightSetZoom(insightSet.bounds));
|
|
134
222
|
}
|
|
135
223
|
|
|
136
224
|
highlightActiveInsight(): void {
|
|
@@ -139,76 +227,28 @@ export class SidebarInsightsTab extends HTMLElement {
|
|
|
139
227
|
}
|
|
140
228
|
|
|
141
229
|
// Find the right set for this insight via the set key.
|
|
142
|
-
const set = this
|
|
230
|
+
const set = this.element.shadowRoot?.querySelector<UI.Widget.WidgetElement<SidebarSingleInsightSet>>(
|
|
143
231
|
`[data-insight-set-key="${this.#activeInsight.insightSetKey}"]`);
|
|
144
232
|
set?.getWidget()?.highlightActiveInsight();
|
|
145
233
|
}
|
|
146
234
|
|
|
147
|
-
|
|
235
|
+
override performUpdate(): void {
|
|
148
236
|
if (!this.#parsedTrace?.insights) {
|
|
149
|
-
Lit.render(Lit.nothing, this.#shadow, {host: this});
|
|
150
237
|
return;
|
|
151
238
|
}
|
|
152
239
|
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
activeInsight: this.#activeInsight,
|
|
167
|
-
parsedTrace: this.#parsedTrace,
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
const contents = html`
|
|
171
|
-
<devtools-widget
|
|
172
|
-
data-insight-set-key=${id}
|
|
173
|
-
.widgetConfig=${UI.Widget.widgetConfig(SidebarSingleInsightSet, {data})}
|
|
174
|
-
></devtools-widget>
|
|
175
|
-
`;
|
|
176
|
-
|
|
177
|
-
if (hasMultipleInsightSets) {
|
|
178
|
-
return html`<details
|
|
179
|
-
?open=${id === this.#selectedInsightSetKey}
|
|
180
|
-
>
|
|
181
|
-
<summary
|
|
182
|
-
@click=${() => this.#insightSetToggled(id)}
|
|
183
|
-
@mouseenter=${() => this.#insightSetHovered(id)}
|
|
184
|
-
@mouseleave=${() => this.#insightSetUnhovered()}
|
|
185
|
-
title=${url.href}>
|
|
186
|
-
${this.#renderDropdownIcon(id === this.#selectedInsightSetKey)}
|
|
187
|
-
<span>${labels[index]}</span>
|
|
188
|
-
<span class='zoom-button' @click=${(event: Event) => this.#onZoomClick(event, id)}>${this.#renderZoomButton(id === this.#selectedInsightSetKey)}</span>
|
|
189
|
-
</summary>
|
|
190
|
-
${contents}
|
|
191
|
-
</details>`;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
return contents;
|
|
195
|
-
})}
|
|
196
|
-
</div>
|
|
197
|
-
`;
|
|
198
|
-
// clang-format on
|
|
199
|
-
|
|
200
|
-
// Insight components contain state, so to prevent insights from previous trace loads breaking things we use the parsedTrace
|
|
201
|
-
// as a render key.
|
|
202
|
-
// Note: newer Lit has `keyed`, but we don't have that, so we do it manually. https://lit.dev/docs/templates/directives/#keyed
|
|
203
|
-
const result = Lit.Directives.repeat([contents], () => this.#parsedTrace, template => template);
|
|
204
|
-
Lit.render(result, this.#shadow, {host: this});
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
customElements.define('devtools-performance-sidebar-insights', SidebarInsightsTab);
|
|
209
|
-
|
|
210
|
-
declare global {
|
|
211
|
-
interface HTMLElementTagNameMap {
|
|
212
|
-
'devtools-performance-sidebar-insights': SidebarInsightsTab;
|
|
240
|
+
const insightSets = [...this.#parsedTrace.insights.values()];
|
|
241
|
+
const input: ViewInput = {
|
|
242
|
+
parsedTrace: this.#parsedTrace,
|
|
243
|
+
labels: Utils.Helpers.createUrlLabels(insightSets.map(({url}) => url)),
|
|
244
|
+
activeInsightSet: this.#selectedInsightSet,
|
|
245
|
+
activeInsight: this.#activeInsight,
|
|
246
|
+
selectedCategory: this.#selectedCategory,
|
|
247
|
+
onInsightSetToggled: this.#onInsightSetToggled.bind(this),
|
|
248
|
+
onInsightSetHovered: this.#onInsightSetHovered.bind(this),
|
|
249
|
+
onInsightSetUnhovered: this.#onInsightSetUnhovered.bind(this),
|
|
250
|
+
onZoomClick: this.#onZoomClick.bind(this),
|
|
251
|
+
};
|
|
252
|
+
this.#view(input, undefined, this.contentElement);
|
|
213
253
|
}
|
|
214
254
|
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
// Copyright 2024 The Chromium Authors
|
|
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
|
-
/* eslint-disable @devtools/no-lit-render-outside-of-view */
|
|
5
4
|
|
|
6
5
|
import * as i18n from '../../../core/i18n/i18n.js';
|
|
7
|
-
import
|
|
6
|
+
import * as Buttons from '../../../ui/components/buttons/buttons.js';
|
|
8
7
|
import * as UI from '../../../ui/legacy/legacy.js';
|
|
9
8
|
import * as Lit from '../../../ui/lit/lit.js';
|
|
10
9
|
|
|
@@ -33,74 +32,96 @@ export interface CategoryData {
|
|
|
33
32
|
title: string;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
|
-
|
|
37
|
-
total: number;
|
|
35
|
+
interface ViewInput {
|
|
38
36
|
rangeStart: number;
|
|
39
37
|
rangeEnd: number;
|
|
38
|
+
total: number;
|
|
40
39
|
categories: CategoryData[];
|
|
41
|
-
selectedEvents: Trace.Types.Events.Event[];
|
|
42
40
|
}
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;
|
|
43
|
+
|
|
44
|
+
export const CATEGORY_SUMMARY_DEFAULT_VIEW: View = (input, _output, target): void => {
|
|
45
|
+
// clang-format off
|
|
46
|
+
render(html`
|
|
47
|
+
<style>${timelineSummaryStyles}</style>
|
|
48
|
+
<style>@scope to (devtools-widget > *) { ${UI.inspectorCommonStyles} }</style>
|
|
49
|
+
<style>@scope to (devtools-widget > *) { ${Buttons.textButtonStyles} }</style>
|
|
50
|
+
<div class="timeline-summary">
|
|
51
|
+
<div class="summary-range">${i18nString(UIStrings.rangeSS, {PH1: i18n.TimeUtilities.millisToString(input.rangeStart), PH2: i18n.TimeUtilities.millisToString(input.rangeEnd)})}</div>
|
|
52
|
+
<div class="category-summary">
|
|
53
|
+
${input.categories.map(category => {
|
|
54
|
+
return html`
|
|
55
|
+
<div class="category-row">
|
|
56
|
+
<div class="category-swatch" style="background-color: ${category.color};"></div>
|
|
57
|
+
<div class="category-name">${category.title}</div>
|
|
58
|
+
<div class="category-value">
|
|
59
|
+
${i18n.TimeUtilities.preciseMillisToString(category.value)}
|
|
60
|
+
<div class="background-bar-container">
|
|
61
|
+
<div class="background-bar" style='width: ${(category.value * 100 / input.total).toFixed(1)}%;'></div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>`;
|
|
65
|
+
})}
|
|
66
|
+
<div class="category-row">
|
|
67
|
+
<div class="category-swatch"></div>
|
|
68
|
+
<div class="category-name">${i18nString(UIStrings.total)}</div>
|
|
69
|
+
<div class="category-value">
|
|
70
|
+
${i18n.TimeUtilities.preciseMillisToString(input.total)}
|
|
71
|
+
<div class="background-bar-container">
|
|
72
|
+
<div class="background-bar"></div>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
|
|
80
|
+
</div>`, target);
|
|
81
|
+
// clang-format on
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export class CategorySummary extends UI.Widget.Widget {
|
|
85
|
+
#view: View;
|
|
47
86
|
|
|
48
87
|
#rangeStart = 0;
|
|
49
88
|
#rangeEnd = 0;
|
|
50
89
|
#total = 0;
|
|
51
90
|
#categories: CategoryData[] = [];
|
|
52
91
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
this.#
|
|
56
|
-
this
|
|
57
|
-
this.#rangeEnd = data.rangeEnd;
|
|
58
|
-
this.#render();
|
|
92
|
+
constructor(view?: View) {
|
|
93
|
+
super();
|
|
94
|
+
this.#view = view ?? CATEGORY_SUMMARY_DEFAULT_VIEW;
|
|
95
|
+
this.requestUpdate();
|
|
59
96
|
}
|
|
60
97
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
<div class="summary-range">${i18nString(UIStrings.rangeSS, {PH1: i18n.TimeUtilities.millisToString(this.#rangeStart), PH2: i18n.TimeUtilities.millisToString(this.#rangeEnd)})}</div>
|
|
66
|
-
<div class="category-summary">
|
|
67
|
-
${this.#categories.map(category => {
|
|
68
|
-
return html`
|
|
69
|
-
<div class="category-row">
|
|
70
|
-
<div class="category-swatch" style="background-color: ${category.color};"></div>
|
|
71
|
-
<div class="category-name">${category.title}</div>
|
|
72
|
-
<div class="category-value">
|
|
73
|
-
${i18n.TimeUtilities.preciseMillisToString(category.value)}
|
|
74
|
-
<div class="background-bar-container">
|
|
75
|
-
<div class="background-bar" style='width: ${(category.value * 100 / this.#total).toFixed(1)}%;'></div>
|
|
76
|
-
</div>
|
|
77
|
-
</div>
|
|
78
|
-
</div>`;
|
|
79
|
-
})}
|
|
80
|
-
<div class="category-row">
|
|
81
|
-
<div class="category-swatch"></div>
|
|
82
|
-
<div class="category-name">${i18nString(UIStrings.total)}</div>
|
|
83
|
-
<div class="category-value">
|
|
84
|
-
${i18n.TimeUtilities.preciseMillisToString(this.#total)}
|
|
85
|
-
<div class="background-bar-container">
|
|
86
|
-
<div class="background-bar"></div>
|
|
87
|
-
</div>
|
|
88
|
-
</div>
|
|
89
|
-
</div>
|
|
90
|
-
</div>
|
|
91
|
-
</div>
|
|
92
|
-
</div>
|
|
98
|
+
set total(total: number) {
|
|
99
|
+
this.#total = total;
|
|
100
|
+
this.requestUpdate();
|
|
101
|
+
}
|
|
93
102
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
103
|
+
set rangeStart(rangeStart: number) {
|
|
104
|
+
this.#rangeStart = rangeStart;
|
|
105
|
+
this.requestUpdate();
|
|
97
106
|
}
|
|
98
|
-
}
|
|
99
107
|
|
|
100
|
-
|
|
108
|
+
set rangeEnd(rangeEnd: number) {
|
|
109
|
+
this.#rangeEnd = rangeEnd;
|
|
110
|
+
this.requestUpdate();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
set categories(categories: CategoryData[]) {
|
|
114
|
+
this.#categories = categories;
|
|
115
|
+
this.requestUpdate();
|
|
116
|
+
}
|
|
101
117
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
118
|
+
override performUpdate(): void {
|
|
119
|
+
const viewInput: ViewInput = {
|
|
120
|
+
rangeStart: this.#rangeStart,
|
|
121
|
+
rangeEnd: this.#rangeEnd,
|
|
122
|
+
total: this.#total,
|
|
123
|
+
categories: this.#categories,
|
|
124
|
+
};
|
|
125
|
+
this.#view(viewInput, undefined, this.contentElement);
|
|
105
126
|
}
|
|
106
127
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
// Copyright 2024 The Chromium Authors
|
|
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
|
-
/* eslint-disable @devtools/no-imperative-dom-api */
|
|
5
4
|
|
|
6
5
|
import '../../../../ui/components/markdown_view/markdown_view.js';
|
|
7
6
|
|
|
@@ -70,7 +69,7 @@ interface ViewInput {
|
|
|
70
69
|
model: InsightModel;
|
|
71
70
|
selected: boolean;
|
|
72
71
|
isAIAssistanceContext: boolean;
|
|
73
|
-
|
|
72
|
+
showAskAI: boolean;
|
|
74
73
|
estimatedSavingsString: string|null;
|
|
75
74
|
estimatedSavingsAriaLabel: string|null;
|
|
76
75
|
renderContent: () => Lit.LitTemplate;
|
|
@@ -89,7 +88,7 @@ const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
89
88
|
estimatedSavingsString,
|
|
90
89
|
estimatedSavingsAriaLabel,
|
|
91
90
|
isAIAssistanceContext,
|
|
92
|
-
|
|
91
|
+
showAskAI,
|
|
93
92
|
dispatchInsightToggle,
|
|
94
93
|
renderContent,
|
|
95
94
|
onHeaderKeyDown,
|
|
@@ -122,7 +121,7 @@ const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
122
121
|
<div class="insight-body">
|
|
123
122
|
<div class="insight-description">${md(model.description)}</div>
|
|
124
123
|
<div class="insight-content">${content}</div>
|
|
125
|
-
${
|
|
124
|
+
${showAskAI ? html`
|
|
126
125
|
<div class="ask-ai-btn-wrap">
|
|
127
126
|
<devtools-button class="ask-ai"
|
|
128
127
|
.variant=${Buttons.Button.Variant.OUTLINED}
|
|
@@ -206,9 +205,6 @@ export abstract class BaseInsightComponent<T extends InsightModel> extends UI.Wi
|
|
|
206
205
|
#view: View;
|
|
207
206
|
abstract internalName: string;
|
|
208
207
|
|
|
209
|
-
// This flag tracks if the Insights AI feature is enabled within Chrome for
|
|
210
|
-
// the active user.
|
|
211
|
-
#askAiEnabled = false;
|
|
212
208
|
// Tracks if this component is rendered withing the AI assistance panel.
|
|
213
209
|
// Currently only relevant to GreenDev.
|
|
214
210
|
#isAIAssistanceContext = false;
|
|
@@ -246,16 +242,6 @@ export abstract class BaseInsightComponent<T extends InsightModel> extends UI.Wi
|
|
|
246
242
|
return false;
|
|
247
243
|
}
|
|
248
244
|
|
|
249
|
-
override wasShown(): void {
|
|
250
|
-
super.wasShown();
|
|
251
|
-
|
|
252
|
-
// Used for unit test purposes when querying the DOM.
|
|
253
|
-
this.element.dataset.insightName = this.internalName;
|
|
254
|
-
|
|
255
|
-
const {devToolsAiAssistancePerformanceAgent} = Root.Runtime.hostConfig;
|
|
256
|
-
this.#askAiEnabled = Boolean(devToolsAiAssistancePerformanceAgent?.enabled);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
245
|
set isAIAssistanceContext(isAIAssistanceContext: boolean) {
|
|
260
246
|
this.#isAIAssistanceContext = isAIAssistanceContext;
|
|
261
247
|
this.requestUpdate();
|
|
@@ -300,11 +286,11 @@ export abstract class BaseInsightComponent<T extends InsightModel> extends UI.Wi
|
|
|
300
286
|
this.requestUpdate();
|
|
301
287
|
}
|
|
302
288
|
|
|
303
|
-
set agentFocus(agentFocus: AIAssistance.AIContext.AgentFocus) {
|
|
289
|
+
set agentFocus(agentFocus: AIAssistance.AIContext.AgentFocus|null) {
|
|
304
290
|
this.#agentFocus = agentFocus;
|
|
305
291
|
}
|
|
306
292
|
|
|
307
|
-
set fieldMetrics(fieldMetrics: Trace.Insights.Common.CrUXFieldMetricResults) {
|
|
293
|
+
set fieldMetrics(fieldMetrics: Trace.Insights.Common.CrUXFieldMetricResults|null) {
|
|
308
294
|
this.#fieldMetrics = fieldMetrics;
|
|
309
295
|
}
|
|
310
296
|
|
|
@@ -422,7 +408,7 @@ export abstract class BaseInsightComponent<T extends InsightModel> extends UI.Wi
|
|
|
422
408
|
estimatedSavingsString: this.getEstimatedSavingsString(),
|
|
423
409
|
estimatedSavingsAriaLabel: this.#getEstimatedSavingsAriaLabel(),
|
|
424
410
|
isAIAssistanceContext: this.#isAIAssistanceContext,
|
|
425
|
-
|
|
411
|
+
showAskAI: this.#canShowAskAI(),
|
|
426
412
|
dispatchInsightToggle: () => this.#dispatchInsightToggle(),
|
|
427
413
|
renderContent: () => this.renderContent(),
|
|
428
414
|
onHeaderKeyDown: () => this.#onHeaderKeyDown,
|
|
@@ -527,14 +513,19 @@ export abstract class BaseInsightComponent<T extends InsightModel> extends UI.Wi
|
|
|
527
513
|
}
|
|
528
514
|
|
|
529
515
|
#canShowAskAI(): boolean {
|
|
530
|
-
if (this.#isAIAssistanceContext) {
|
|
516
|
+
if (this.#isAIAssistanceContext || !this.hasAskAiSupport()) {
|
|
531
517
|
return false;
|
|
532
518
|
}
|
|
533
519
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
520
|
+
// Check if the Insights AI feature enabled within Chrome for the active user.
|
|
521
|
+
const {devToolsAiAssistancePerformanceAgent} = Root.Runtime.hostConfig;
|
|
522
|
+
const askAiEnabled = Boolean(devToolsAiAssistancePerformanceAgent?.enabled);
|
|
523
|
+
if (!askAiEnabled) {
|
|
524
|
+
return false;
|
|
525
|
+
}
|
|
537
526
|
|
|
538
|
-
|
|
527
|
+
const {aidaAvailability} = Root.Runtime.hostConfig;
|
|
528
|
+
return aidaAvailability?.enterprisePolicyValue !== Root.Runtime.GenAiEnterprisePolicyValue.DISABLE &&
|
|
529
|
+
aidaAvailability?.enabled === true;
|
|
539
530
|
}
|
|
540
531
|
}
|