chrome-devtools-frontend 1.0.1596260 → 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.
Files changed (94) hide show
  1. package/front_end/core/common/Console.ts +6 -6
  2. package/front_end/core/common/Settings.ts +12 -8
  3. package/front_end/core/root/DevToolsContext.ts +13 -7
  4. package/front_end/core/sdk/CPUThrottlingManager.ts +12 -9
  5. package/front_end/core/sdk/FrameManager.ts +7 -8
  6. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +1 -1
  7. package/front_end/entrypoints/main/MainImpl.ts +1 -2
  8. package/front_end/foundation/Universe.ts +22 -11
  9. package/front_end/generated/InspectorBackendCommands.ts +7 -0
  10. package/front_end/generated/protocol-mapping.d.ts +16 -0
  11. package/front_end/generated/protocol-proxy-api.d.ts +25 -0
  12. package/front_end/generated/protocol.ts +71 -0
  13. package/front_end/models/ai_assistance/AiConversation.ts +6 -95
  14. package/front_end/models/ai_assistance/agents/AiAgent.ts +11 -1
  15. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +14 -0
  16. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +1 -0
  17. package/front_end/panels/ai_assistance/components/ChatMessage.ts +25 -4
  18. package/front_end/panels/ai_assistance/components/WalkthroughView.ts +2 -2
  19. package/front_end/panels/elements/StandaloneStylesContainer.ts +48 -12
  20. package/front_end/panels/elements/StylePropertiesSection.ts +97 -1
  21. package/front_end/panels/elements/StylePropertyTreeElement.ts +16 -0
  22. package/front_end/panels/elements/StylesContainer.ts +1 -0
  23. package/front_end/panels/elements/StylesSidebarPane.ts +48 -32
  24. package/front_end/panels/elements/stylePropertiesTreeOutline.css +1 -1
  25. package/front_end/panels/lighthouse/LighthouseController.ts +13 -5
  26. package/front_end/panels/lighthouse/LighthousePanel.ts +22 -5
  27. package/front_end/panels/recorder/components/StepView.ts +0 -2
  28. package/front_end/panels/recorder/components/stepView.css +13 -13
  29. package/front_end/panels/recorder/components/timelineSection.css +6 -7
  30. package/front_end/panels/timeline/TimelinePanel.ts +18 -0
  31. package/front_end/panels/timeline/components/CWVMetrics.ts +339 -0
  32. package/front_end/panels/timeline/components/Sidebar.ts +17 -0
  33. package/front_end/panels/timeline/components/SidebarInsightsTab.ts +10 -0
  34. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +25 -267
  35. package/front_end/panels/timeline/components/components.ts +2 -0
  36. package/front_end/panels/timeline/components/cwvMetrics.css +107 -0
  37. package/front_end/panels/timeline/components/sidebarSingleInsightSet.css +0 -102
  38. package/front_end/panels/timeline/timeline-meta.ts +11 -0
  39. package/front_end/panels/timeline/utils/Helpers.ts +5 -1
  40. package/front_end/third_party/chromium/README.chromium +1 -1
  41. package/front_end/third_party/puppeteer/README.chromium +2 -2
  42. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts +7 -0
  43. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts.map +1 -1
  44. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js.map +1 -1
  45. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts +1 -0
  46. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts.map +1 -1
  47. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js +3 -0
  48. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js.map +1 -1
  49. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts +1 -0
  50. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts.map +1 -1
  51. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js +6 -0
  52. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js.map +1 -1
  53. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts +1 -0
  54. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts.map +1 -1
  55. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js +5 -0
  56. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js.map +1 -1
  57. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  58. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
  59. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
  60. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  61. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  62. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  63. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +7 -0
  64. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +14 -3
  65. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts +7 -0
  66. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts.map +1 -1
  67. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js.map +1 -1
  68. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts +1 -0
  69. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts.map +1 -1
  70. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js +3 -0
  71. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js.map +1 -1
  72. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts +1 -0
  73. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts.map +1 -1
  74. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js +6 -0
  75. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js.map +1 -1
  76. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts +1 -0
  77. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts.map +1 -1
  78. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js +5 -0
  79. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js.map +1 -1
  80. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
  81. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
  82. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  83. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  84. package/front_end/third_party/puppeteer/package/lib/types.d.ts +7 -0
  85. package/front_end/third_party/puppeteer/package/package.json +1 -1
  86. package/front_end/third_party/puppeteer/package/src/api/Page.ts +8 -0
  87. package/front_end/third_party/puppeteer/package/src/bidi/Page.ts +4 -0
  88. package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +7 -0
  89. package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +6 -0
  90. package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
  91. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  92. package/front_end/ui/components/text_editor/AutocompleteHistory.ts +89 -5
  93. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  94. package/package.json +1 -1
@@ -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.