UNPKG

chrome-devtools-frontend

Version:
160 lines (144 loc) • 5.57 kB
// Copyright 2025 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import './CollapsibleAssistanceContentWidget.js'; import './PerformanceAgentFlameChart.js'; import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js'; import * as Logs from '../../../models/logs/logs.js'; import * as NetworkTimeCalculator from '../../../models/network_time_calculator/network_time_calculator.js'; import * as Trace from '../../../models/trace/trace.js'; import * as UI from '../../../ui/legacy/legacy.js'; import * as Lit from '../../../ui/lit/lit.js'; import * as Network from '../../network/network.js'; import * as Insights from '../../timeline/components/insights/insights.js'; import artifactsViewerStyles from './artifactsViewer.css.js'; import type * as PerformanceAgentFlameChart from './PerformanceAgentFlameChart.js'; const {html, render} = Lit; export interface ViewInput { artifacts: AiAssistanceModel.ArtifactsManager.Artifact[]; parsedTrace: Trace.TraceModel.ParsedTrace; } export function renderArtifact( artifact: AiAssistanceModel.ArtifactsManager.Artifact, parsedTrace: Trace.TraceModel.ParsedTrace): Lit.LitTemplate { switch (artifact.type) { // clang-format off case 'insight': { const insightRenderer = new Insights.InsightRenderer.InsightRenderer(); const componentName = artifact.insightType; const insightSet = parsedTrace.insights?.values().next().value; const insightModel = insightSet?.model[componentName as Trace.Insights.Types.InsightKeys]; if (!insightModel) { return Lit.nothing; } return html` <devtools-collapsible-assistance-content-widget .data=${{headerText: `Insight - ${componentName}`}}> ${insightRenderer.renderInsightToWidgetElement(parsedTrace, insightSet, insightModel, componentName, { selected: true, isAIAssistanceContext: true, })} </devtools-collapsible-assistance-content-widget>`; } case 'network-request': { const networkRequest = artifact.request; if ('args' in networkRequest && Trace.Types.Events.isSyntheticNetworkRequest(networkRequest)) { const calculator = new NetworkTimeCalculator.NetworkTimeCalculator(true); const sdkRequest = Logs.NetworkLog.NetworkLog.instance() .requestsForId(networkRequest.args.data.requestId) .find(r => r.url() === networkRequest.args.data.url) ?? null; if (!sdkRequest) { return Lit.nothing; } return html` <devtools-collapsible-assistance-content-widget .data=${{headerText: `Network Request: ${ sdkRequest.url().length > 80 ? sdkRequest.url().slice(0, 80) + '...' : sdkRequest.url()}`}}> <devtools-widget class="actions" .widgetConfig=${UI.Widget.widgetConfig(Network.RequestTimingView.RequestTimingView, { request: sdkRequest, calculator, })}></devtools-widget> </devtools-collapsible-assistance-content-widget>`; } return Lit.nothing; } case 'flamechart': { return html` <devtools-collapsible-assistance-content-widget .data=${{headerText: `Flamechart`}}> <devtools-performance-agent-flame-chart .data=${{ parsedTrace, start: artifact.start, end: artifact.end, } as PerformanceAgentFlameChart.PerformanceAgentFlameChartData}> </devtools-performance-agent-flame-chart> </devtools-collapsible-assistance-content-widget>`; } default: return Lit.nothing; // clang-format on } } export const DEFAULT_VIEW = ( input: ViewInput, _output: Record<string, unknown>, target: HTMLElement, ): void => { // clang-format off render( html` <style>${artifactsViewerStyles}</style> <div class="artifacts-viewer"> ${input.artifacts.map(artifact => renderArtifact(artifact, input.parsedTrace))} </div> `, target ); // clang-format on }; export type View = typeof DEFAULT_VIEW; export class ArtifactsViewer extends UI.Widget.Widget { #view: View; #parsedTrace: Trace.TraceModel.ParsedTrace|null; constructor(element?: HTMLElement, view = DEFAULT_VIEW) { super(element); this.#view = view; this.#parsedTrace = null; } override wasShown(): void { super.wasShown(); AiAssistanceModel.ArtifactsManager.ArtifactsManager.instance().addEventListener( AiAssistanceModel.ArtifactsManager.ArtifactAddedEvent.eventName, () => { if (this.#parsedTrace) { this.performUpdate(); } }, ); UI.Context.Context.instance().addFlavorChangeListener( AiAssistanceModel.AIContext.AgentFocus, ({data}) => { this.#parsedTrace = data.parsedTrace; if (this.#parsedTrace) { this.performUpdate(); } }, ); const focus = UI.Context.Context.instance().flavor(AiAssistanceModel.AIContext.AgentFocus); if (focus) { this.#parsedTrace = focus.parsedTrace; this.performUpdate(); } } override performUpdate(): void { if (!this.#parsedTrace) { return; } this.#view( { artifacts: AiAssistanceModel.ArtifactsManager.ArtifactsManager.instance().artifacts, parsedTrace: this.#parsedTrace, }, {}, this.contentElement, ); } }