chrome-devtools-frontend 1.0.1595925 → 1.0.1596535
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/.stylelintrc.json +3 -1
- package/docs/ui_engineering.md +0 -36
- package/front_end/core/common/Console.ts +6 -6
- package/front_end/core/common/Settings.ts +12 -8
- package/front_end/core/host/UserMetrics.ts +0 -1
- package/front_end/core/root/DevToolsContext.ts +13 -7
- package/front_end/core/root/ExperimentNames.ts +0 -1
- package/front_end/core/sdk/CPUThrottlingManager.ts +12 -9
- package/front_end/core/sdk/DOMModel.ts +206 -0
- package/front_end/core/sdk/FrameManager.ts +7 -8
- package/front_end/core/sdk/SourceMapScopesInfo.ts +1 -1
- package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +1 -1
- package/front_end/entrypoints/greendev_floaty/floaty.css +2 -2
- package/front_end/entrypoints/main/MainImpl.ts +1 -7
- package/front_end/foundation/Universe.ts +22 -11
- package/front_end/generated/InspectorBackendCommands.ts +7 -0
- package/front_end/generated/protocol-mapping.d.ts +16 -0
- package/front_end/generated/protocol-proxy-api.d.ts +25 -0
- package/front_end/generated/protocol.ts +71 -0
- package/front_end/models/ai_assistance/AiConversation.ts +6 -95
- package/front_end/models/ai_assistance/ConversationHandler.ts +1 -1
- package/front_end/models/ai_assistance/agents/AiAgent.ts +11 -1
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +14 -0
- package/front_end/models/ai_code_completion/AiCodeCompletion.ts +1 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +3 -0
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +73 -35
- package/front_end/panels/ai_assistance/components/WalkthroughView.ts +30 -6
- package/front_end/panels/ai_assistance/components/chatMessage.css +4 -2
- package/front_end/panels/application/IndexedDBModel.ts +2 -4
- package/front_end/panels/application/ServiceWorkersView.ts +3 -11
- package/front_end/panels/developer_resources/DeveloperResourcesListView.ts +1 -1
- package/front_end/panels/elements/ComputedStyleWidget.ts +25 -16
- package/front_end/panels/elements/ElementsPanel.ts +0 -1
- package/front_end/panels/elements/StandaloneStylesContainer.ts +48 -12
- package/front_end/panels/elements/StylePropertiesSection.ts +97 -1
- package/front_end/panels/elements/StylePropertyTreeElement.ts +27 -5
- package/front_end/panels/elements/StylesContainer.ts +1 -0
- package/front_end/panels/elements/StylesSidebarPane.ts +48 -32
- package/front_end/panels/elements/nodeStackTraceWidget.css +1 -1
- package/front_end/panels/elements/stylePropertiesTreeOutline.css +1 -1
- package/front_end/panels/emulation/DeviceModeToolbar.ts +171 -89
- package/front_end/panels/greendev/GreenDevPanel.css +2 -2
- package/front_end/panels/issues/AffectedPermissionElementsView.ts +9 -6
- package/front_end/panels/lighthouse/LighthouseController.ts +13 -5
- package/front_end/panels/lighthouse/LighthousePanel.ts +22 -5
- package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +11 -3
- package/front_end/panels/recorder/components/StepView.ts +0 -2
- package/front_end/panels/recorder/components/stepView.css +13 -13
- package/front_end/panels/recorder/components/timelineSection.css +6 -7
- package/front_end/panels/settings/components/SyncSection.ts +4 -13
- package/front_end/panels/timeline/CompatibilityTracksAppender.ts +2 -15
- package/front_end/panels/timeline/RecordingMetadata.ts +1 -1
- package/front_end/panels/timeline/TimelineDetailsView.ts +2 -2
- package/front_end/panels/timeline/TimelinePanel.ts +18 -0
- package/front_end/panels/timeline/TimelineUIUtils.ts +7 -4
- package/front_end/panels/timeline/components/CWVMetrics.ts +339 -0
- package/front_end/panels/timeline/components/Sidebar.ts +17 -0
- package/front_end/panels/timeline/components/SidebarInsightsTab.ts +10 -0
- package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +25 -267
- package/front_end/panels/timeline/components/Utils.ts +2 -2
- package/front_end/panels/timeline/components/components.ts +2 -0
- package/front_end/panels/timeline/components/cwvMetrics.css +107 -0
- package/front_end/panels/timeline/components/sidebarSingleInsightSet.css +0 -102
- package/front_end/panels/timeline/timeline-meta.ts +11 -0
- package/front_end/panels/timeline/utils/Helpers.ts +5 -1
- package/front_end/panels/webauthn/webauthnPane.css +1 -1
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/puppeteer/README.chromium +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts +7 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js +3 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js +6 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js +5 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +7 -0
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +14 -3
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts +7 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js +3 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js +6 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js +5 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/types.d.ts +7 -0
- package/front_end/third_party/puppeteer/package/package.json +1 -1
- package/front_end/third_party/puppeteer/package/src/api/Page.ts +8 -0
- package/front_end/third_party/puppeteer/package/src/bidi/Page.ts +4 -0
- package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +7 -0
- package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +6 -0
- package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
- package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
- package/front_end/ui/components/text_editor/AutocompleteHistory.ts +89 -5
- package/front_end/ui/components/tree_outline/TreeOutline.ts +1 -1
- package/front_end/ui/legacy/ProgressIndicator.ts +1 -1
- package/front_end/ui/legacy/Toolbar.ts +1 -1
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +14 -7
- package/front_end/ui/legacy/legacy.ts +0 -2
- package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
- package/inspector_overlay/loadCSS.rollup.js +2 -2
- package/inspector_overlay/tool_source_order.css +1 -0
- package/package.json +1 -1
- package/front_end/ui/legacy/Fragment.ts +0 -234
|
@@ -13,18 +13,18 @@
|
|
|
13
13
|
.title-container {
|
|
14
14
|
/* Vertically center items with min-width: 0; */
|
|
15
15
|
min-width: 0;
|
|
16
|
-
font-size:
|
|
17
|
-
line-height: 16px;
|
|
18
|
-
letter-spacing: 0.03em;
|
|
16
|
+
font-size: var(--sys-size-7);
|
|
19
17
|
display: flex;
|
|
20
18
|
flex-direction: row;
|
|
21
|
-
gap:
|
|
22
|
-
outline-offset:
|
|
19
|
+
gap: var(--sys-size-2);
|
|
20
|
+
outline-offset: var(--sys-size-2);
|
|
21
|
+
flex-grow: 1;
|
|
22
|
+
align-items: center;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
.action {
|
|
26
26
|
display: flex;
|
|
27
|
-
align-items:
|
|
27
|
+
align-items: center;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
.title {
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
cy: 8px;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
.is-start-of-group .circle-icon {
|
|
61
|
+
.is-start-of-group:not(:first-of-type) .circle-icon {
|
|
62
62
|
r: 7px;
|
|
63
63
|
fill: var(--sys-color-cdt-base-container);
|
|
64
64
|
stroke: var(--sys-color-primary);
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
height: 14px;
|
|
144
144
|
transition: 200ms;
|
|
145
145
|
position: absolute;
|
|
146
|
-
top:
|
|
146
|
+
top: 14px;
|
|
147
147
|
left: 24px;
|
|
148
148
|
transform: rotate(-90deg);
|
|
149
149
|
color: var(--sys-color-on-surface);
|
|
@@ -183,7 +183,11 @@ devtools-recorder-step-editor:hover {
|
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
devtools-recorder-step-editor.is-selected {
|
|
186
|
-
background-color: color-mix(
|
|
186
|
+
background-color: color-mix(
|
|
187
|
+
in srgb,
|
|
188
|
+
var(--sys-color-tonal-container),
|
|
189
|
+
var(--sys-color-cdt-base-container) 50%
|
|
190
|
+
);
|
|
187
191
|
border: 1px solid var(--sys-color-tonal-outline);
|
|
188
192
|
}
|
|
189
193
|
|
|
@@ -192,10 +196,6 @@ devtools-recorder-step-editor.is-selected {
|
|
|
192
196
|
flex-flow: row nowrap;
|
|
193
197
|
}
|
|
194
198
|
|
|
195
|
-
.filler {
|
|
196
|
-
flex-grow: 1;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
199
|
.subtitle {
|
|
200
200
|
font-weight: normal;
|
|
201
201
|
color: var(--sys-color-on-surface-subtle);
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
.timeline-section {
|
|
15
15
|
position: relative;
|
|
16
|
-
padding:
|
|
16
|
+
padding: 8px 0 8px 40px;
|
|
17
17
|
margin-left: 8px;
|
|
18
18
|
|
|
19
19
|
--override-color-recording-successful-text: #36a854;
|
|
@@ -52,12 +52,12 @@
|
|
|
52
52
|
z-index: 4;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
.is-start-of-group {
|
|
56
|
-
padding-top:
|
|
55
|
+
.is-start-of-group:not(:first-of-type) {
|
|
56
|
+
padding-top: 16px;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
.is-end-of-group {
|
|
60
|
-
padding-bottom:
|
|
60
|
+
padding-bottom: 16px;
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
.icon {
|
|
@@ -73,7 +73,7 @@
|
|
|
73
73
|
display: block;
|
|
74
74
|
transform: translateX(-50%);
|
|
75
75
|
top: 18px;
|
|
76
|
-
height:
|
|
76
|
+
height: 100%;
|
|
77
77
|
z-index: 1; /* We want this to be below of `.overlay` for stopped case */
|
|
78
78
|
}
|
|
79
79
|
|
|
@@ -86,8 +86,7 @@
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
.is-first-section .bar {
|
|
89
|
-
|
|
90
|
-
height: calc(100% - 8px);
|
|
89
|
+
height: 100%;
|
|
91
90
|
display: none;
|
|
92
91
|
}
|
|
93
92
|
|
|
@@ -10,10 +10,10 @@ import * as Common from '../../../core/common/common.js';
|
|
|
10
10
|
import * as Host from '../../../core/host/host.js';
|
|
11
11
|
import * as i18n from '../../../core/i18n/i18n.js';
|
|
12
12
|
import type * as Platform from '../../../core/platform/platform.js';
|
|
13
|
-
import * as SDK from '../../../core/sdk/sdk.js';
|
|
14
13
|
import * as Badges from '../../../models/badges/badges.js';
|
|
15
14
|
import * as Buttons from '../../../ui/components/buttons/buttons.js';
|
|
16
15
|
import type * as SettingsComponents from '../../../ui/components/settings/settings.js';
|
|
16
|
+
import * as UIHelpers from '../../../ui/helpers/helpers.js';
|
|
17
17
|
import * as UI from '../../../ui/legacy/legacy.js';
|
|
18
18
|
import * as Lit from '../../../ui/lit/lit.js';
|
|
19
19
|
import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
|
|
@@ -343,19 +343,10 @@ export class SyncSection extends UI.Widget.Widget {
|
|
|
343
343
|
}
|
|
344
344
|
|
|
345
345
|
#onWarningClick(event: Event): void {
|
|
346
|
-
const rootTarget = SDK.TargetManager.TargetManager.instance().rootTarget();
|
|
347
|
-
if (rootTarget === null) {
|
|
348
|
-
return;
|
|
349
|
-
}
|
|
350
346
|
// TODO: investigate if /advance link is alive
|
|
351
|
-
const warningLink =
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
void rootTarget.targetAgent().invoke_createTarget({url: warningLink}).then(result => {
|
|
355
|
-
if (result.getError()) {
|
|
356
|
-
Host.InspectorFrontendHost.InspectorFrontendHostInstance.openInNewTab(warningLink);
|
|
357
|
-
}
|
|
358
|
-
});
|
|
347
|
+
const warningLink =
|
|
348
|
+
this.#syncInfo.isSyncActive ? 'chrome://settings/syncSetup/advanced' : 'chrome://settings/syncSetup';
|
|
349
|
+
UIHelpers.openInNewTab(warningLink);
|
|
359
350
|
event.consume();
|
|
360
351
|
}
|
|
361
352
|
|
|
@@ -36,17 +36,6 @@ export interface PopoverInfo {
|
|
|
36
36
|
additionalElements: HTMLElement[];
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
let showPostMessageEvents: boolean|undefined;
|
|
40
|
-
function isShowPostMessageEventsEnabled(): boolean {
|
|
41
|
-
// Everytime the experiment is toggled devtools is reloaded so the
|
|
42
|
-
// cache is updated automatically.
|
|
43
|
-
if (showPostMessageEvents === undefined) {
|
|
44
|
-
showPostMessageEvents =
|
|
45
|
-
Root.Runtime.experiments.isEnabled(Root.ExperimentNames.ExperimentName.TIMELINE_SHOW_POST_MESSAGE_EVENTS);
|
|
46
|
-
}
|
|
47
|
-
return showPostMessageEvents;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
39
|
export function entryIsVisibleInTimeline(
|
|
51
40
|
entry: Trace.Types.Events.Event, parsedTrace?: Trace.TraceModel.ParsedTrace): boolean {
|
|
52
41
|
if (parsedTrace?.data.Meta.traceIsGeneric) {
|
|
@@ -64,10 +53,8 @@ export function entryIsVisibleInTimeline(
|
|
|
64
53
|
return true;
|
|
65
54
|
}
|
|
66
55
|
|
|
67
|
-
if (
|
|
68
|
-
|
|
69
|
-
return true;
|
|
70
|
-
}
|
|
56
|
+
if (Trace.Types.Events.isSchedulePostMessage(entry) || Trace.Types.Events.isHandlePostMessage(entry)) {
|
|
57
|
+
return true;
|
|
71
58
|
}
|
|
72
59
|
|
|
73
60
|
if (Trace.Types.Extensions.isSyntheticExtensionEntry(entry)) {
|
|
@@ -39,7 +39,7 @@ async function innerForTraceCalculate({recordingStartTime, cruxFieldData}: DataF
|
|
|
39
39
|
const deviceModeModel = EmulationModel.DeviceModeModel.DeviceModeModel.tryInstance();
|
|
40
40
|
let emulatedDeviceTitle;
|
|
41
41
|
if (deviceModeModel?.type() === EmulationModel.DeviceModeModel.Type.Device) {
|
|
42
|
-
emulatedDeviceTitle = deviceModeModel.device()?.title
|
|
42
|
+
emulatedDeviceTitle = deviceModeModel.device()?.title;
|
|
43
43
|
} else if (deviceModeModel?.type() === EmulationModel.DeviceModeModel.Type.Responsive) {
|
|
44
44
|
emulatedDeviceTitle = 'Responsive';
|
|
45
45
|
}
|
|
@@ -149,7 +149,7 @@ export class TimelineDetailsPane extends
|
|
|
149
149
|
this.#thirdPartyTree.addEventListener(TimelineTreeView.Events.TREE_ROW_HOVERED, node => {
|
|
150
150
|
// Re-dispatch through 3P event to get 3P dimmer.
|
|
151
151
|
this.dispatchEventToListeners(
|
|
152
|
-
TimelineTreeView.Events.TREE_ROW_HOVERED, {node: node.data.node, events: node.data.events
|
|
152
|
+
TimelineTreeView.Events.TREE_ROW_HOVERED, {node: node.data.node, events: node.data.events});
|
|
153
153
|
});
|
|
154
154
|
|
|
155
155
|
this.#thirdPartyTree.addEventListener(TimelineTreeView.Events.BOTTOM_UP_BUTTON_CLICKED, node => {
|
|
@@ -158,7 +158,7 @@ export class TimelineDetailsPane extends
|
|
|
158
158
|
this.#thirdPartyTree.addEventListener(TimelineTreeView.Events.TREE_ROW_CLICKED, node => {
|
|
159
159
|
// Re-dispatch through 3P event to get 3P dimmer.
|
|
160
160
|
this.dispatchEventToListeners(
|
|
161
|
-
TimelineTreeView.Events.TREE_ROW_CLICKED, {node: node.data.node, events: node.data.events
|
|
161
|
+
TimelineTreeView.Events.TREE_ROW_CLICKED, {node: node.data.node, events: node.data.events});
|
|
162
162
|
});
|
|
163
163
|
|
|
164
164
|
this.tabbedPane.addEventListener(UI.TabbedPane.Events.TabSelected, this.tabSelected, this);
|
|
@@ -3054,6 +3054,17 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
|
3054
3054
|
this.#setActiveInsight({model: insightModel, insightSetKey}, {highlightInsight: true});
|
|
3055
3055
|
}
|
|
3056
3056
|
|
|
3057
|
+
revealCoreVitals(revealable: Utils.Helpers.RevealableCoreVitals): void {
|
|
3058
|
+
if (this.#splitWidget.showMode() !== UI.SplitWidget.ShowMode.BOTH) {
|
|
3059
|
+
this.#splitWidget.showBoth();
|
|
3060
|
+
}
|
|
3061
|
+
this.#sideBar.openInsightsTab();
|
|
3062
|
+
if (revealable.insightSetKey) {
|
|
3063
|
+
this.#sideBar.setActiveInsightSet(revealable.insightSetKey);
|
|
3064
|
+
this.#setActiveInsight(null);
|
|
3065
|
+
}
|
|
3066
|
+
}
|
|
3067
|
+
|
|
3057
3068
|
static async executeRecordAndReload(): Promise<Trace.TraceModel.ParsedTrace> {
|
|
3058
3069
|
await UI.ViewManager.ViewManager.instance().showView('timeline');
|
|
3059
3070
|
const panelInstance = TimelinePanel.instance();
|
|
@@ -3238,6 +3249,13 @@ export class InsightRevealer implements Common.Revealer.Revealer<Utils.Helpers.R
|
|
|
3238
3249
|
}
|
|
3239
3250
|
}
|
|
3240
3251
|
|
|
3252
|
+
export class CoreVitalsRevealer implements Common.Revealer.Revealer<Utils.Helpers.RevealableCoreVitals> {
|
|
3253
|
+
async reveal(revealable: Utils.Helpers.RevealableCoreVitals): Promise<void> {
|
|
3254
|
+
await UI.ViewManager.ViewManager.instance().showView('timeline');
|
|
3255
|
+
TimelinePanel.instance().revealCoreVitals(revealable);
|
|
3256
|
+
}
|
|
3257
|
+
}
|
|
3258
|
+
|
|
3241
3259
|
export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
|
|
3242
3260
|
handleAction(context: UI.Context.Context, actionId: string): boolean {
|
|
3243
3261
|
const panel = context.flavor(TimelinePanel);
|
|
@@ -34,6 +34,8 @@
|
|
|
34
34
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
35
35
|
*/
|
|
36
36
|
|
|
37
|
+
import '../../ui/kit/kit.js';
|
|
38
|
+
|
|
37
39
|
import * as Common from '../../core/common/common.js';
|
|
38
40
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
39
41
|
import * as Platform from '../../core/platform/platform.js';
|
|
@@ -50,13 +52,13 @@ import * as CodeHighlighter from '../../ui/components/code_highlighter/code_high
|
|
|
50
52
|
// eslint-disable-next-line @devtools/es-modules-import
|
|
51
53
|
import codeHighlighterStyles from '../../ui/components/code_highlighter/codeHighlighter.css.js';
|
|
52
54
|
import * as uiI18n from '../../ui/i18n/i18n.js';
|
|
53
|
-
import {Link} from '../../ui/kit/kit.js';
|
|
54
55
|
import * as PerfUI from '../../ui/legacy/components/perf_ui/perf_ui.js';
|
|
55
56
|
// eslint-disable-next-line @devtools/es-modules-import
|
|
56
57
|
import imagePreviewStyles from '../../ui/legacy/components/utils/imagePreview.css.js';
|
|
57
58
|
import * as LegacyComponents from '../../ui/legacy/components/utils/utils.js';
|
|
58
59
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
59
60
|
import * as ThemeSupport from '../../ui/legacy/theme_support/theme_support.js';
|
|
61
|
+
import {html, render} from '../../ui/lit/lit.js';
|
|
60
62
|
import * as PanelsCommon from '../common/common.js';
|
|
61
63
|
|
|
62
64
|
import {getDurationString} from './AppenderUtils.js';
|
|
@@ -833,9 +835,10 @@ export class TimelineUIUtils {
|
|
|
833
835
|
break;
|
|
834
836
|
}
|
|
835
837
|
|
|
836
|
-
const
|
|
837
|
-
|
|
838
|
-
|
|
838
|
+
const div = document.createElement('div');
|
|
839
|
+
// eslint-disable-next-line @devtools/no-lit-render-outside-of-view
|
|
840
|
+
render(html`<devtools-link href=${link}>${i18nString(UIStrings.learnMore)}</devtools-link> about ${name}.`, div);
|
|
841
|
+
return div;
|
|
839
842
|
}
|
|
840
843
|
|
|
841
844
|
static buildConsumeCacheDetails(
|
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
// Copyright 2026 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
|
+
import * as i18n from '../../../core/i18n/i18n.js';
|
|
6
|
+
import * as Platform from '../../../core/platform/platform.js';
|
|
7
|
+
import * as CrUXManager from '../../../models/crux-manager/crux-manager.js';
|
|
8
|
+
import * as Trace from '../../../models/trace/trace.js';
|
|
9
|
+
import * as Buttons from '../../../ui/components/buttons/buttons.js';
|
|
10
|
+
import * as UI from '../../../ui/legacy/legacy.js';
|
|
11
|
+
import * as Lit from '../../../ui/lit/lit.js';
|
|
12
|
+
import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
|
|
13
|
+
|
|
14
|
+
import cwvMetricsStyles from './cwvMetrics.css.js';
|
|
15
|
+
import {md} from './insights/Helpers.js';
|
|
16
|
+
import * as Insights from './insights/insights.js';
|
|
17
|
+
import {isFieldWorseThanLocal, NumberWithUnit} from './Utils.js';
|
|
18
|
+
|
|
19
|
+
const {html} = Lit.StaticHtml;
|
|
20
|
+
|
|
21
|
+
const UIStrings = {
|
|
22
|
+
/**
|
|
23
|
+
* @description title used for a metric value to tell the user about its score classification
|
|
24
|
+
* @example {INP} PH1
|
|
25
|
+
* @example {1.2s} PH2
|
|
26
|
+
* @example {poor} PH3
|
|
27
|
+
*/
|
|
28
|
+
metricScore: '{PH1}: {PH2} {PH3} score',
|
|
29
|
+
/**
|
|
30
|
+
* @description title used for a metric value to tell the user that the data is unavailable
|
|
31
|
+
* @example {INP} PH1
|
|
32
|
+
*/
|
|
33
|
+
metricScoreUnavailable: '{PH1}: unavailable',
|
|
34
|
+
/**
|
|
35
|
+
* @description Label denoting that metrics were observed in the field, from real use data (CrUX). Also denotes if from URL or Origin dataset.
|
|
36
|
+
* @example {URL} PH1
|
|
37
|
+
*/
|
|
38
|
+
fieldScoreLabel: 'Field ({PH1})',
|
|
39
|
+
/**
|
|
40
|
+
* @description Label for an option that selects the page's specific URL as opposed to it's entire origin/domain.
|
|
41
|
+
*/
|
|
42
|
+
urlOption: 'URL',
|
|
43
|
+
/**
|
|
44
|
+
* @description Label for an option that selects the page's entire origin/domain as opposed to it's specific URL.
|
|
45
|
+
*/
|
|
46
|
+
originOption: 'Origin',
|
|
47
|
+
/**
|
|
48
|
+
* @description Title for button that closes a warning popup.
|
|
49
|
+
*/
|
|
50
|
+
dismissTitle: 'Dismiss',
|
|
51
|
+
/**
|
|
52
|
+
* @description Title shown in a warning dialog when field metrics (collected from real users) is worse than the locally observed metrics.
|
|
53
|
+
*/
|
|
54
|
+
fieldMismatchTitle: 'Field & local metrics mismatch',
|
|
55
|
+
/**
|
|
56
|
+
* @description Text shown in a warning dialog when field metrics (collected from real users) is worse than the locally observed metrics.
|
|
57
|
+
* Asks user to use features such as throttling and device emulation.
|
|
58
|
+
*/
|
|
59
|
+
fieldMismatchNotice:
|
|
60
|
+
'There are many reasons why local and field metrics [may not match](https://web.dev/articles/lab-and-field-data-differences). ' +
|
|
61
|
+
'Adjust [throttling settings and device emulation](https://developer.chrome.com/docs/devtools/device-mode) to analyze traces more similar to the average user\'s environment.',
|
|
62
|
+
} as const;
|
|
63
|
+
|
|
64
|
+
const str_ = i18n.i18n.registerUIStrings('panels/timeline/components/CWVMetrics.ts', UIStrings);
|
|
65
|
+
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
66
|
+
|
|
67
|
+
interface LocalMetrics {
|
|
68
|
+
lcp: {value: Trace.Types.Timing.Micro, event: Trace.Types.Events.AnyLargestContentfulPaintCandidate}|null;
|
|
69
|
+
cls: {value: number, worstClusterEvent: Trace.Types.Events.Event|null};
|
|
70
|
+
inp: {value: Trace.Types.Timing.Micro, event: Trace.Types.Events.SyntheticInteractionPair}|null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getLocalMetrics(parsedTrace: Trace.TraceModel.ParsedTrace|null, insightSetKey: string|null): LocalMetrics|
|
|
74
|
+
null {
|
|
75
|
+
if (!parsedTrace || !insightSetKey) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const insightSet = parsedTrace.insights?.get(insightSetKey);
|
|
80
|
+
if (!insightSet) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const lcp = Trace.Insights.Common.getLCP(insightSet);
|
|
85
|
+
const cls = Trace.Insights.Common.getCLS(insightSet);
|
|
86
|
+
const inp = Trace.Insights.Common.getINP(insightSet);
|
|
87
|
+
|
|
88
|
+
return {lcp, cls, inp};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function getFieldMetrics(parsedTrace: Trace.TraceModel.ParsedTrace|null, insightSetKey: string|null):
|
|
92
|
+
Trace.Insights.Common.CrUXFieldMetricResults|null {
|
|
93
|
+
if (!parsedTrace || !parsedTrace.metadata?.cruxFieldData || !insightSetKey) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const insightSet = parsedTrace.insights?.get(insightSetKey);
|
|
98
|
+
if (!insightSet) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
let scope: CrUXManager.Scope|null = null;
|
|
103
|
+
try {
|
|
104
|
+
scope = CrUXManager.CrUXManager.instance().getSelectedScope();
|
|
105
|
+
} catch {
|
|
106
|
+
// test environment
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const fieldMetricsResults =
|
|
110
|
+
Trace.Insights.Common.getFieldMetricsForInsightSet(insightSet, parsedTrace.metadata, scope);
|
|
111
|
+
if (!fieldMetricsResults) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return fieldMetricsResults;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
interface MetricsViewInput {
|
|
119
|
+
parsedTrace: Trace.TraceModel.ParsedTrace|null;
|
|
120
|
+
insightSetKey: string|null;
|
|
121
|
+
didDismissFieldMismatchNotice: boolean;
|
|
122
|
+
onDismisFieldMismatchNotice: () => void;
|
|
123
|
+
onClickMetric: (traceEvent: Trace.Types.Events.Event) => void;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
type MetricsView = (input: MetricsViewInput, output: undefined, target: HTMLElement) => void;
|
|
127
|
+
|
|
128
|
+
const CWV_METRICS_VIEW: MetricsView = (input, _output, target) => {
|
|
129
|
+
const {
|
|
130
|
+
parsedTrace,
|
|
131
|
+
insightSetKey,
|
|
132
|
+
didDismissFieldMismatchNotice,
|
|
133
|
+
onDismisFieldMismatchNotice,
|
|
134
|
+
onClickMetric,
|
|
135
|
+
} = input;
|
|
136
|
+
|
|
137
|
+
const local = getLocalMetrics(parsedTrace, insightSetKey);
|
|
138
|
+
const field = getFieldMetrics(parsedTrace, insightSetKey);
|
|
139
|
+
|
|
140
|
+
const localValues = {
|
|
141
|
+
lcp: local?.lcp?.value !== undefined ? Trace.Helpers.Timing.microToMilli(local?.lcp.value) : undefined,
|
|
142
|
+
inp: local?.inp?.value !== undefined ? Trace.Helpers.Timing.microToMilli(local?.inp.value) : undefined,
|
|
143
|
+
};
|
|
144
|
+
const fieldValues = field && {
|
|
145
|
+
lcp: field.lcp?.value !== undefined ? Trace.Helpers.Timing.microToMilli(field.lcp.value) : undefined,
|
|
146
|
+
inp: field.inp?.value !== undefined ? Trace.Helpers.Timing.microToMilli(field.inp.value) : undefined,
|
|
147
|
+
};
|
|
148
|
+
const showFieldMismatchNotice =
|
|
149
|
+
!didDismissFieldMismatchNotice && !!fieldValues && isFieldWorseThanLocal(localValues, fieldValues);
|
|
150
|
+
|
|
151
|
+
function renderMetricValue(
|
|
152
|
+
metric: 'LCP'|'CLS'|'INP', value: number|null, relevantEvent: Trace.Types.Events.Event|null): Lit.LitTemplate {
|
|
153
|
+
let valueText: string;
|
|
154
|
+
let valueDisplay: HTMLElement|string;
|
|
155
|
+
let classification;
|
|
156
|
+
if (value === null) {
|
|
157
|
+
valueText = valueDisplay = '-';
|
|
158
|
+
classification = Trace.Handlers.ModelHandlers.PageLoadMetrics.ScoreClassification.UNCLASSIFIED;
|
|
159
|
+
} else if (metric === 'LCP') {
|
|
160
|
+
const micros = value as Trace.Types.Timing.Micro;
|
|
161
|
+
const {text, element} = NumberWithUnit.formatMicroSecondsAsSeconds(micros);
|
|
162
|
+
valueText = text;
|
|
163
|
+
valueDisplay = element;
|
|
164
|
+
classification =
|
|
165
|
+
Trace.Handlers.ModelHandlers.PageLoadMetrics.scoreClassificationForLargestContentfulPaint(micros);
|
|
166
|
+
} else if (metric === 'CLS') {
|
|
167
|
+
valueText = valueDisplay = value ? value.toFixed(2) : '0';
|
|
168
|
+
classification = Trace.Handlers.ModelHandlers.LayoutShifts.scoreClassificationForLayoutShift(value);
|
|
169
|
+
} else if (metric === 'INP') {
|
|
170
|
+
const micros = value as Trace.Types.Timing.Micro;
|
|
171
|
+
const {text, element} = NumberWithUnit.formatMicroSecondsAsMillisFixed(micros);
|
|
172
|
+
valueText = text;
|
|
173
|
+
valueDisplay = element;
|
|
174
|
+
classification =
|
|
175
|
+
Trace.Handlers.ModelHandlers.UserInteractions.scoreClassificationForInteractionToNextPaint(micros);
|
|
176
|
+
} else {
|
|
177
|
+
Platform.TypeScriptUtilities.assertNever(metric, `Unexpected metric ${metric}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// NOTE: it is deliberate to use the same value for the title and
|
|
181
|
+
// aria-label; the aria-label is used to give more context to
|
|
182
|
+
// screen-readers, and the title is to aid users who may not know what
|
|
183
|
+
// the red/orange/green classification is, or those who are unable to
|
|
184
|
+
// easily distinguish the visual colour differences.
|
|
185
|
+
// clang-format off
|
|
186
|
+
const title = value !== null ?
|
|
187
|
+
i18nString(UIStrings.metricScore, {PH1: metric, PH2: valueText, PH3: classification}) :
|
|
188
|
+
i18nString(UIStrings.metricScoreUnavailable, {PH1: metric});
|
|
189
|
+
|
|
190
|
+
return html`
|
|
191
|
+
<button class="metric"
|
|
192
|
+
@click=${relevantEvent ? onClickMetric.bind(relevantEvent) : null}
|
|
193
|
+
title=${title}
|
|
194
|
+
aria-label=${title}
|
|
195
|
+
>
|
|
196
|
+
<div class="metric-value metric-value-${classification}">${valueDisplay}</div>
|
|
197
|
+
</button>
|
|
198
|
+
`;
|
|
199
|
+
// clang-format on
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const lcpEl = renderMetricValue('LCP', local?.lcp?.value ?? null, local?.lcp?.event ?? null);
|
|
203
|
+
const inpEl = renderMetricValue('INP', local?.inp?.value ?? null, local?.inp?.event ?? null);
|
|
204
|
+
const clsEl = renderMetricValue('CLS', local?.cls?.value ?? null, local?.cls?.worstClusterEvent ?? null);
|
|
205
|
+
|
|
206
|
+
const localMetricsTemplateResult = html`
|
|
207
|
+
<div class="metrics-row">
|
|
208
|
+
<span>${lcpEl}</span>
|
|
209
|
+
<span>${inpEl}</span>
|
|
210
|
+
<span>${clsEl}</span>
|
|
211
|
+
<span class="row-label">Local</span>
|
|
212
|
+
</div>
|
|
213
|
+
<span class="row-border"></span>
|
|
214
|
+
`;
|
|
215
|
+
|
|
216
|
+
let fieldMetricsTemplateResult;
|
|
217
|
+
if (field) {
|
|
218
|
+
const {lcp, inp, cls} = field;
|
|
219
|
+
|
|
220
|
+
const lcpEl = renderMetricValue('LCP', lcp?.value ?? null, null);
|
|
221
|
+
const inpEl = renderMetricValue('INP', inp?.value ?? null, null);
|
|
222
|
+
const clsEl = renderMetricValue('CLS', cls?.value ?? null, null);
|
|
223
|
+
|
|
224
|
+
let scope = i18nString(UIStrings.originOption);
|
|
225
|
+
if (lcp?.pageScope === 'url' || inp?.pageScope === 'url') {
|
|
226
|
+
scope = i18nString(UIStrings.urlOption);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// clang-format off
|
|
230
|
+
fieldMetricsTemplateResult = html`
|
|
231
|
+
<div class="metrics-row">
|
|
232
|
+
<span>${lcpEl}</span>
|
|
233
|
+
<span>${inpEl}</span>
|
|
234
|
+
<span>${clsEl}</span>
|
|
235
|
+
<span class="row-label">${i18nString(UIStrings.fieldScoreLabel, {PH1: scope})}</span>
|
|
236
|
+
</div>
|
|
237
|
+
<span class="row-border"></span>
|
|
238
|
+
`;
|
|
239
|
+
// clang-format on
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
let fieldIsDifferentEl;
|
|
243
|
+
if (showFieldMismatchNotice) {
|
|
244
|
+
// clang-format off
|
|
245
|
+
fieldIsDifferentEl = html`
|
|
246
|
+
<div class="field-mismatch-notice" jslog=${VisualLogging.section('timeline.insights.field-mismatch')}>
|
|
247
|
+
<h3>${i18nString(UIStrings.fieldMismatchTitle)}</h3>
|
|
248
|
+
<devtools-button
|
|
249
|
+
title=${i18nString(UIStrings.dismissTitle)}
|
|
250
|
+
.iconName=${'cross'}
|
|
251
|
+
.variant=${Buttons.Button.Variant.ICON}
|
|
252
|
+
.jslogContext=${'timeline.insights.dismiss-field-mismatch'}
|
|
253
|
+
@click=${onDismisFieldMismatchNotice}
|
|
254
|
+
></devtools-button>
|
|
255
|
+
<div class="field-mismatch-notice__body">${md(i18nString(UIStrings.fieldMismatchNotice))}</div>
|
|
256
|
+
</div>
|
|
257
|
+
`;
|
|
258
|
+
// clang-format on
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const classes = {metrics: true, 'metrics--field': Boolean(fieldMetricsTemplateResult)};
|
|
262
|
+
const metricsTableEl = html`<div class=${Lit.Directives.classMap(classes)}>
|
|
263
|
+
<div class="metrics-row">
|
|
264
|
+
<span class="metric-label">LCP</span>
|
|
265
|
+
<span class="metric-label">INP</span>
|
|
266
|
+
<span class="metric-label">CLS</span>
|
|
267
|
+
<span class="row-label"></span>
|
|
268
|
+
</div>
|
|
269
|
+
${localMetricsTemplateResult}
|
|
270
|
+
${fieldMetricsTemplateResult}
|
|
271
|
+
</div>`;
|
|
272
|
+
|
|
273
|
+
Lit.render(
|
|
274
|
+
html`
|
|
275
|
+
<style>${cwvMetricsStyles}</style>
|
|
276
|
+
${metricsTableEl}
|
|
277
|
+
${fieldIsDifferentEl}
|
|
278
|
+
`,
|
|
279
|
+
target);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
export interface CWVMetricsData {
|
|
283
|
+
insightSetKey: Trace.Types.Events.NavigationId|null;
|
|
284
|
+
parsedTrace: Trace.TraceModel.ParsedTrace|null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export class CWVMetrics extends UI.Widget.Widget {
|
|
288
|
+
#view: MetricsView;
|
|
289
|
+
#data: CWVMetricsData = {
|
|
290
|
+
insightSetKey: null,
|
|
291
|
+
parsedTrace: null,
|
|
292
|
+
};
|
|
293
|
+
|
|
294
|
+
#didDismissFieldMismatchNotice = false;
|
|
295
|
+
|
|
296
|
+
constructor(element?: HTMLElement, view: MetricsView = CWV_METRICS_VIEW) {
|
|
297
|
+
super(element, {useShadowDom: true});
|
|
298
|
+
this.#view = view;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
set data(data: CWVMetricsData) {
|
|
302
|
+
this.#data = data;
|
|
303
|
+
this.requestUpdate();
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
#onClickMetric(traceEvent: Trace.Types.Events.Event): void {
|
|
307
|
+
this.element.dispatchEvent(new Insights.EventRef.EventReferenceClick(traceEvent));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
#onDismisFieldMismatchNotice(): void {
|
|
311
|
+
this.#didDismissFieldMismatchNotice = true;
|
|
312
|
+
this.requestUpdate();
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
override performUpdate(): void {
|
|
316
|
+
const {
|
|
317
|
+
parsedTrace,
|
|
318
|
+
insightSetKey,
|
|
319
|
+
} = this.#data;
|
|
320
|
+
|
|
321
|
+
if (!parsedTrace?.insights || !insightSetKey || !(parsedTrace.insights instanceof Map)) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const insightSet = parsedTrace.insights.get(insightSetKey);
|
|
326
|
+
if (!insightSet) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const input: MetricsViewInput = {
|
|
331
|
+
parsedTrace,
|
|
332
|
+
insightSetKey,
|
|
333
|
+
didDismissFieldMismatchNotice: this.#didDismissFieldMismatchNotice,
|
|
334
|
+
onDismisFieldMismatchNotice: this.#onDismisFieldMismatchNotice.bind(this),
|
|
335
|
+
onClickMetric: this.#onClickMetric.bind(this),
|
|
336
|
+
};
|
|
337
|
+
this.#view(input, undefined, this.contentElement);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
@@ -163,6 +163,14 @@ export class SidebarWidget extends UI.Widget.VBox {
|
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
openInsightsTab(): void {
|
|
167
|
+
this.#tabbedPane.selectTab(SidebarTabs.INSIGHTS);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
setActiveInsightSet(insightSetKey: string): void {
|
|
171
|
+
this.#insightsView.setActiveInsightSet(insightSetKey);
|
|
172
|
+
}
|
|
173
|
+
|
|
166
174
|
/**
|
|
167
175
|
* True if the sidebar has been visible at least one time. This is persisted
|
|
168
176
|
* to the user settings so it persists across sessions. This is used because
|
|
@@ -213,6 +221,15 @@ class InsightsView extends UI.Widget.VBox {
|
|
|
213
221
|
});
|
|
214
222
|
}
|
|
215
223
|
}
|
|
224
|
+
|
|
225
|
+
setActiveInsightSet(insightSetKey: string): void {
|
|
226
|
+
const widget = this.#component.getWidget();
|
|
227
|
+
if (!widget) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
widget.setActiveInsightSet(insightSetKey);
|
|
232
|
+
}
|
|
216
233
|
}
|
|
217
234
|
|
|
218
235
|
class AnnotationsView extends UI.Widget.VBox {
|
|
@@ -200,6 +200,16 @@ export class SidebarInsightsTab extends UI.Widget.Widget {
|
|
|
200
200
|
this.requestUpdate();
|
|
201
201
|
}
|
|
202
202
|
|
|
203
|
+
setActiveInsightSet(insightSetKey: string): void {
|
|
204
|
+
if (this.#parsedTrace?.insights) {
|
|
205
|
+
const insightSet = this.#parsedTrace.insights.get(insightSetKey);
|
|
206
|
+
if (insightSet) {
|
|
207
|
+
this.#selectedInsightSet = insightSet;
|
|
208
|
+
this.requestUpdate();
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
203
213
|
#onInsightSetToggled(insightSet: Trace.Insights.Types.InsightSet): void {
|
|
204
214
|
this.#selectedInsightSet = this.#selectedInsightSet === insightSet ? null : insightSet;
|
|
205
215
|
// Update the active insight set.
|