chrome-devtools-frontend 1.0.1598808 → 1.0.1601661
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/.agents/README.md +13 -0
- package/{agents/prompts/creating-a-model.md → .agents/skills/creating-a-model/SKILL.md} +7 -1
- package/{agents/prompts/devtools-imports.md → .agents/skills/devtools-imports/SKILL.md} +10 -5
- package/{agents/prompts/merging-devtools-module.md → .agents/skills/merging-devtools-module/SKILL.md} +5 -0
- package/{agents/prompts/ui-widgets.md → .agents/skills/ui-widgets/SKILL.md} +5 -0
- package/{agents/prompts/verification.md → .agents/skills/verification/SKILL.md} +5 -0
- package/front_end/core/common/Settings.ts +24 -1
- package/front_end/core/dom_extension/DOMExtension.ts +1 -0
- package/front_end/core/host/AidaClient.ts +5 -2
- package/front_end/core/host/AidaGcaTranslation.ts +377 -0
- package/front_end/core/host/GcaTypes.ts +520 -0
- package/front_end/core/host/UserMetrics.ts +0 -3
- package/front_end/core/host/host.ts +4 -0
- package/front_end/core/root/ExperimentNames.ts +0 -3
- package/front_end/core/sdk/CPUThrottlingManager.ts +12 -9
- package/front_end/core/sdk/ResourceTreeModel.ts +1 -1
- package/front_end/core/sdk/SourceMap.ts +4 -2
- package/front_end/entrypoints/main/MainImpl.ts +1 -12
- package/front_end/generated/Deprecation.ts +16 -0
- package/front_end/generated/InspectorBackendCommands.ts +4 -6
- package/front_end/generated/protocol.ts +46 -77
- package/front_end/models/ai_assistance/AiConversation.ts +49 -22
- package/front_end/models/ai_assistance/AiHistoryStorage.snapshot.txt +1 -1
- package/front_end/models/ai_assistance/AiHistoryStorage.ts +1 -0
- package/front_end/models/ai_assistance/ConversationHandler.ts +16 -11
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +76 -36
- package/front_end/models/ai_assistance/agents/AiAgent.ts +26 -2
- package/front_end/models/ai_assistance/agents/BreakpointDebuggerAgent.ts +0 -1
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +11 -0
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +39 -0
- package/front_end/models/ai_assistance/agents/ConversationSummaryAgent.ts +53 -12
- package/front_end/models/ai_assistance/agents/FileAgent.ts +0 -15
- package/front_end/models/ai_assistance/agents/NetworkAgent.ts +0 -5
- package/front_end/models/ai_assistance/agents/PerformanceAgent.snapshot.txt +57 -0
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +87 -15
- package/front_end/models/ai_assistance/agents/PerformanceAnnotationsAgent.ts +0 -10
- package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +0 -6
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +1 -5
- package/front_end/models/ai_assistance/ai_assistance.ts +2 -0
- package/front_end/models/ai_assistance/data_formatters/LighthouseFormatter.snapshot.txt +84 -0
- package/front_end/models/ai_assistance/data_formatters/LighthouseFormatter.ts +172 -0
- package/front_end/models/breakpoints/BreakpointManager.ts +20 -12
- package/front_end/models/emulation/DeviceModeModel.ts +5 -1
- package/front_end/models/emulation/EmulatedDevices.ts +3 -2
- package/front_end/models/javascript_metadata/NativeFunctions.js +60 -0
- package/front_end/models/lighthouse/LighthouseReporterTypes.ts +104 -10
- package/front_end/models/lighthouse/RunTypes.ts +42 -0
- package/front_end/models/lighthouse/lighthouse.ts +2 -0
- package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +59 -35
- package/front_end/models/stack_trace/StackTrace.ts +5 -0
- package/front_end/models/stack_trace/StackTraceImpl.ts +7 -1
- package/front_end/models/stack_trace/StackTraceModel.ts +2 -1
- package/front_end/models/stack_trace/stack_trace.ts +4 -0
- package/front_end/models/trace/ModelImpl.ts +4 -0
- package/front_end/models/trace/helpers/SamplesIntegrator.ts +1 -8
- package/front_end/models/trace/types/Configuration.ts +0 -5
- package/front_end/panels/accessibility/AccessibilityNodeView.ts +7 -3
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +163 -74
- package/front_end/panels/ai_assistance/ExportConversation.ts +25 -0
- package/front_end/panels/ai_assistance/ai_assistance.ts +2 -0
- package/front_end/panels/ai_assistance/components/ChatInput.ts +2 -0
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +120 -32
- package/front_end/panels/ai_assistance/components/ChatView.ts +44 -0
- package/front_end/panels/ai_assistance/components/ExportForAgentsDialog.ts +240 -0
- package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +4 -12
- package/front_end/panels/ai_assistance/components/StylingAgentMarkdownRenderer.ts +5 -27
- package/front_end/panels/ai_assistance/components/WalkthroughView.ts +115 -17
- package/front_end/panels/ai_assistance/components/chatMessage.css +65 -1
- package/front_end/panels/ai_assistance/components/chatView.css +8 -0
- package/front_end/panels/ai_assistance/components/exportForAgentsDialog.css +82 -0
- package/front_end/panels/ai_assistance/components/walkthroughView.css +9 -1
- package/front_end/panels/animation/AnimationTimeline.ts +5 -5
- package/front_end/panels/application/AppManifestView.ts +13 -7
- package/front_end/panels/application/FrameDetailsView.ts +4 -4
- package/front_end/panels/application/preloading/PreloadingView.ts +2 -4
- package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +6 -3
- package/front_end/panels/application/preloading/components/PreloadingString.ts +15 -2
- package/front_end/panels/application/preloading/helper/PreloadingForward.ts +31 -2
- package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +3 -2
- package/front_end/panels/common/DOMLinkifier.ts +10 -8
- package/front_end/panels/console/ConsoleView.ts +24 -2
- package/front_end/panels/console/ConsoleViewMessage.ts +22 -23
- package/front_end/panels/console/console.ts +0 -2
- package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +5 -5
- package/front_end/panels/elements/ElementsTreeElement.ts +2 -3
- package/front_end/panels/elements/PropertiesWidget.ts +5 -3
- package/front_end/panels/elements/StandaloneStylesContainer.ts +21 -9
- package/front_end/panels/elements/StylePropertiesSection.ts +98 -50
- package/front_end/panels/elements/StylePropertyTreeElement.ts +38 -4
- package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +43 -35
- package/front_end/panels/elements/StylesSidebarPane.ts +154 -24
- package/front_end/panels/elements/components/ComputedStyleTrace.ts +3 -2
- package/front_end/panels/event_listeners/EventListenersView.ts +16 -4
- package/front_end/panels/explain/components/ConsoleInsight.ts +6 -9
- package/front_end/panels/explain/explain-meta.ts +5 -0
- package/front_end/panels/issues/AffectedResourcesView.ts +5 -9
- package/front_end/panels/issues/AffectedSelectivePermissionsInterventionView.ts +1 -1
- package/front_end/panels/lighthouse/LighthouseController.ts +154 -164
- package/front_end/panels/lighthouse/LighthousePanel.ts +5 -9
- package/front_end/panels/lighthouse/LighthouseProtocolService.ts +1 -0
- package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +9 -6
- package/front_end/panels/lighthouse/LighthouseStartView.ts +7 -6
- package/front_end/panels/lighthouse/LighthouseStatusView.ts +32 -8
- package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +7 -3
- package/front_end/panels/network/NetworkDataGridNode.ts +34 -0
- package/front_end/panels/network/NetworkLogViewColumns.ts +10 -0
- package/front_end/panels/network/RequestPayloadView.ts +4 -1
- package/front_end/panels/network/RequestTimingView.ts +8 -2
- package/front_end/panels/performance_monitor/performanceMonitor.css +6 -4
- package/front_end/panels/recorder/RecorderController.ts +52 -14
- package/front_end/panels/recorder/components/RecordingView.ts +2 -1
- package/front_end/panels/recorder/models/RecordingStorage.ts +15 -20
- package/front_end/panels/sensors/SensorsView.ts +333 -157
- package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +18 -4
- package/front_end/panels/sources/DebuggerPausedMessage.ts +6 -6
- package/front_end/panels/sources/FilteredUISourceCodeListProvider.ts +2 -3
- package/front_end/panels/sources/NavigatorView.ts +11 -7
- package/front_end/panels/sources/ScopeChainSidebarPane.ts +1 -1
- package/front_end/panels/sources/SourcesPanel.ts +12 -34
- package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +5 -1
- package/front_end/panels/sources/sources-meta.ts +6 -0
- package/front_end/panels/timeline/StatusDialog.ts +159 -83
- package/front_end/panels/timeline/ThirdPartyTreeView.ts +6 -2
- package/front_end/panels/timeline/TimelineController.ts +0 -4
- package/front_end/panels/timeline/TimelineDetailsView.ts +35 -3
- package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +6 -1
- package/front_end/panels/timeline/TimelineFlameChartNetworkDataProvider.ts +9 -4
- package/front_end/panels/timeline/TimelinePanel.ts +36 -11
- package/front_end/panels/timeline/TimelineTreeView.ts +14 -3
- package/front_end/panels/timeline/TimelineUIUtils.ts +5 -57
- package/front_end/panels/timeline/components/InteractionBreakdown.ts +2 -1
- package/front_end/panels/timeline/components/LiveMetricsView.ts +2 -2
- package/front_end/panels/timeline/components/NetworkRequestTooltip.ts +3 -7
- package/front_end/panels/timeline/components/Sidebar.ts +11 -19
- package/front_end/panels/timeline/components/SidebarInsightsTab.ts +2 -1
- package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +60 -27
- package/front_end/panels/timeline/components/TimelineRangeSummaryView.ts +66 -0
- package/front_end/panels/timeline/components/TimelineSummary.ts +7 -19
- package/front_end/panels/timeline/components/components.ts +2 -0
- package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +55 -28
- package/front_end/panels/timeline/components/insights/NodeLink.ts +3 -3
- package/front_end/panels/timeline/components/insights/baseInsightComponent.css +7 -0
- package/front_end/panels/timeline/components/insights/insights.ts +0 -2
- package/front_end/panels/timeline/components/timelineRangeSummaryView.css +20 -0
- package/front_end/panels/timeline/overlays/OverlaysImpl.ts +7 -7
- package/front_end/panels/timeline/timeline-meta.ts +11 -0
- package/front_end/panels/timeline/timelineDetailsView.css +0 -9
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/web-vitals/package/dist/modules/attribution/onINP.js +11 -2
- package/front_end/third_party/web-vitals/package/src/attribution/onINP.ts +11 -2
- package/front_end/third_party/web-vitals/patches/0001-Add-onEachInteraction-to-onINP-options.patch +65 -4
- package/front_end/ui/kit/link/link.css +2 -2
- package/front_end/ui/legacy/Widget.ts +5 -3
- package/front_end/ui/legacy/components/object_ui/CustomPreviewComponent.ts +7 -2
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +57 -35
- package/front_end/ui/legacy/components/source_frame/PreviewFactory.ts +1 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +6 -1
- package/package.json +1 -1
- package/agents/prompts/README.md +0 -18
- package/front_end/panels/timeline/components/insights/InsightRenderer.ts +0 -93
- /package/front_end/{panels/console → models/stack_trace}/ErrorStackParser.ts +0 -0
|
@@ -8,13 +8,13 @@ import '../../../ui/kit/kit.js';
|
|
|
8
8
|
import * as Common from '../../../core/common/common.js';
|
|
9
9
|
import * as Host from '../../../core/host/host.js';
|
|
10
10
|
import * as i18n from '../../../core/i18n/i18n.js';
|
|
11
|
-
import
|
|
11
|
+
import * as Platform from '../../../core/platform/platform.js';
|
|
12
12
|
import * as Root from '../../../core/root/root.js';
|
|
13
13
|
import * as SDK from '../../../core/sdk/sdk.js';
|
|
14
14
|
import type * as Protocol from '../../../generated/protocol.js';
|
|
15
15
|
import type {
|
|
16
|
-
AiWidget, ComputedStyleAiWidget, CoreVitalsAiWidget, DomTreeAiWidget,
|
|
17
|
-
'../../../models/ai_assistance/agents/AiAgent.js';
|
|
16
|
+
AiWidget, ComputedStyleAiWidget, CoreVitalsAiWidget, DomTreeAiWidget, LcpBreakdownAiWidget, PerformanceTraceAiWidget,
|
|
17
|
+
StylePropertiesAiWidget} from '../../../models/ai_assistance/agents/AiAgent.js';
|
|
18
18
|
import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
|
|
19
19
|
import * as ComputedStyle from '../../../models/computed_style/computed_style.js';
|
|
20
20
|
import * as Marked from '../../../third_party/marked/marked.js';
|
|
@@ -28,7 +28,10 @@ import * as Lit from '../../../ui/lit/lit.js';
|
|
|
28
28
|
import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
|
|
29
29
|
import * as Elements from '../../elements/elements.js';
|
|
30
30
|
import * as TimelineComponents from '../../timeline/components/components.js';
|
|
31
|
+
import * as TimelineInsights from '../../timeline/components/insights/insights.js';
|
|
32
|
+
import * as Timeline from '../../timeline/timeline.js';
|
|
31
33
|
import * as TimelineUtils from '../../timeline/utils/utils.js';
|
|
34
|
+
import {PanelUtils} from '../../utils/utils.js';
|
|
32
35
|
|
|
33
36
|
import chatMessageStyles from './chatMessage.css.js';
|
|
34
37
|
import {walkthroughTitle, WalkthroughView} from './WalkthroughView.js';
|
|
@@ -174,7 +177,11 @@ const UIStringsNotTranslate = {
|
|
|
174
177
|
/**
|
|
175
178
|
* @description Title for the button that takes the user into other DevTools panels to reveal items the AI references.
|
|
176
179
|
*/
|
|
177
|
-
reveal: 'Reveal'
|
|
180
|
+
reveal: 'Reveal',
|
|
181
|
+
/**
|
|
182
|
+
* @description Title used for revealing the performance trace.
|
|
183
|
+
*/
|
|
184
|
+
revealTrace: 'Reveal trace'
|
|
178
185
|
} as const;
|
|
179
186
|
|
|
180
187
|
export interface Step {
|
|
@@ -522,8 +529,8 @@ function renderWalkthroughSidebarButton(
|
|
|
522
529
|
/**
|
|
523
530
|
* Responsible for rendering the AI Walkthrough UI. This can take different
|
|
524
531
|
* shapes and involve different parts depending on if the walkthrough is
|
|
525
|
-
* inlined, expanded, or if we have side-effect steps
|
|
526
|
-
*
|
|
532
|
+
* inlined, expanded, or if we have side-effect steps. In cases where the
|
|
533
|
+
* walkthrough is closed, side-effect steps are rendered inline in the chat.
|
|
527
534
|
*/
|
|
528
535
|
function renderWalkthroughUI(input: ChatMessageViewInput, steps: Step[]): Lit.LitTemplate {
|
|
529
536
|
const lastStep = steps.at(-1);
|
|
@@ -537,11 +544,17 @@ function renderWalkthroughUI(input: ChatMessageViewInput, steps: Step[]): Lit.Li
|
|
|
537
544
|
const openWalkThroughSidebarButton =
|
|
538
545
|
!input.walkthrough.isInlined ? renderWalkthroughSidebarButton(input, steps) : Lit.nothing;
|
|
539
546
|
|
|
540
|
-
//
|
|
541
|
-
//
|
|
542
|
-
//
|
|
547
|
+
// A message's walkthrough is considered expanded if the walkthrough is
|
|
548
|
+
// open and it is specifically targeting this message. This is necessary
|
|
549
|
+
// because the walkthrough state is shared across all messages in the chat.
|
|
550
|
+
const isExpanded = (input.walkthrough.isExpanded && input.walkthrough.activeMessage === input.message);
|
|
551
|
+
|
|
552
|
+
// When a side-effect step is present, it's shown in the main chat UI if the
|
|
553
|
+
// walkthrough is closed, allowing the user to approve it without opening
|
|
554
|
+
// the walkthrough. If the walkthrough is already open, the side-effect
|
|
555
|
+
// step is displayed within the walkthrough instead.
|
|
543
556
|
// clang-format off
|
|
544
|
-
const sideEffectStepsUI = !
|
|
557
|
+
const sideEffectStepsUI = !isExpanded && sideEffectSteps.length > 0 ? sideEffectSteps.map(step => html`
|
|
545
558
|
<div class="side-effect-container">
|
|
546
559
|
${renderStep({
|
|
547
560
|
step,
|
|
@@ -552,11 +565,6 @@ function renderWalkthroughUI(input: ChatMessageViewInput, steps: Step[]): Lit.Li
|
|
|
552
565
|
</div> `) : Lit.nothing;
|
|
553
566
|
// clang-format on
|
|
554
567
|
|
|
555
|
-
// If the walkthrough is inlined (narrow width screens), render it here.
|
|
556
|
-
// Note that we force it open if there is a side-effect.
|
|
557
|
-
|
|
558
|
-
const isExpanded = (input.walkthrough.isExpanded && input.walkthrough.activeMessage === input.message) ||
|
|
559
|
-
steps.some(s => s.requestApproval);
|
|
560
568
|
// clang-format off
|
|
561
569
|
const walkthroughInline = input.walkthrough.isInlined ? html`
|
|
562
570
|
<div class="walkthrough-container">
|
|
@@ -574,8 +582,8 @@ function renderWalkthroughUI(input: ChatMessageViewInput, steps: Step[]): Lit.Li
|
|
|
574
582
|
|
|
575
583
|
return html`
|
|
576
584
|
${openWalkThroughSidebarButton}
|
|
577
|
-
${sideEffectStepsUI}
|
|
578
585
|
${walkthroughInline}
|
|
586
|
+
${sideEffectStepsUI}
|
|
579
587
|
`;
|
|
580
588
|
// clang-format on
|
|
581
589
|
}
|
|
@@ -644,8 +652,10 @@ export function renderStep({step, isLoading, markdownRenderer, isLast}: {
|
|
|
644
652
|
}
|
|
645
653
|
|
|
646
654
|
interface WidgetMakerResponse {
|
|
647
|
-
|
|
655
|
+
// Can be null if the widget is only used to add the Reveal CTA.
|
|
656
|
+
renderedWidget: Lit.LitTemplate|null;
|
|
648
657
|
revealable: unknown;
|
|
658
|
+
customRevealTitle?: Platform.UIString.LocalizedString;
|
|
649
659
|
}
|
|
650
660
|
|
|
651
661
|
const nodeCache = new Map<Protocol.DOM.BackendNodeId, SDK.DOMModel.DOMNode>();
|
|
@@ -720,6 +730,26 @@ async function makeStylePropertiesWidget(widgetData: StylePropertiesAiWidget): P
|
|
|
720
730
|
return {renderedWidget, revealable: domNodeForId};
|
|
721
731
|
}
|
|
722
732
|
|
|
733
|
+
async function makeLcpBreakdownWidget(widgetData: LcpBreakdownAiWidget): Promise<WidgetMakerResponse|null> {
|
|
734
|
+
const insight = widgetData.data.lcpData;
|
|
735
|
+
if (!insight) {
|
|
736
|
+
return null;
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
const widgetConfig = UI.Widget.widgetConfig(TimelineInsights.LCPBreakdown.LCPBreakdown, {
|
|
740
|
+
model: insight,
|
|
741
|
+
minimal: true,
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
// clang-format off
|
|
745
|
+
const renderedWidget = html`<devtools-widget
|
|
746
|
+
class="lcp-breakdown-widget"
|
|
747
|
+
.widgetConfig=${widgetConfig}></devtools-widget>`;
|
|
748
|
+
// clang-format on
|
|
749
|
+
|
|
750
|
+
return {renderedWidget, revealable: new TimelineUtils.Helpers.RevealableInsight(insight)};
|
|
751
|
+
}
|
|
752
|
+
|
|
723
753
|
function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTemplate {
|
|
724
754
|
if (response === null) {
|
|
725
755
|
return Lit.nothing;
|
|
@@ -732,24 +762,64 @@ function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTempla
|
|
|
732
762
|
void Common.Revealer.reveal(response?.revealable);
|
|
733
763
|
}
|
|
734
764
|
|
|
765
|
+
const classes = Lit.Directives.classMap({
|
|
766
|
+
'widget-and-revealer-container': true,
|
|
767
|
+
'revealer-only': response.renderedWidget === null,
|
|
768
|
+
});
|
|
735
769
|
// clang-format off
|
|
736
770
|
return html`
|
|
737
|
-
<div class
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
771
|
+
<div class=${classes}>
|
|
772
|
+
${response.renderedWidget ? html`
|
|
773
|
+
<div class="widget-content-container">
|
|
774
|
+
${response.renderedWidget}
|
|
775
|
+
</div>` : Lit.nothing
|
|
776
|
+
}
|
|
741
777
|
<div class="widget-reveal-container">
|
|
742
778
|
<devtools-button class="widget-reveal"
|
|
743
779
|
.iconName=${'tab-move'}
|
|
744
780
|
.variant=${Buttons.Button.Variant.TEXT}
|
|
745
781
|
@click=${onReveal}
|
|
746
|
-
>${lockedString(UIStringsNotTranslate.reveal)}</devtools-button>
|
|
782
|
+
>${response.customRevealTitle ?? lockedString(UIStringsNotTranslate.reveal)}</devtools-button>
|
|
747
783
|
</div>
|
|
748
784
|
</div>
|
|
749
785
|
`;
|
|
750
786
|
// clang-format on
|
|
751
787
|
}
|
|
752
788
|
|
|
789
|
+
async function makePerformanceTraceWidget(widgetData: PerformanceTraceAiWidget): Promise<WidgetMakerResponse|null> {
|
|
790
|
+
return {
|
|
791
|
+
renderedWidget: null,
|
|
792
|
+
revealable: new Timeline.TimelinePanel.ParsedTraceRevealable(widgetData.data.parsedTrace),
|
|
793
|
+
customRevealTitle: lockedString(UIStringsNotTranslate.revealTrace),
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function renderNetworkRequestPreview(networkRequest: NonNullable<DomTreeAiWidget['data']['networkRequest']>):
|
|
798
|
+
Lit.TemplateResult {
|
|
799
|
+
const filename = networkRequest.url.split('/').pop() || networkRequest.url;
|
|
800
|
+
const size = i18n.ByteUtilities.bytesToString(networkRequest.size);
|
|
801
|
+
const resourceType = Common.ResourceType.resourceTypes[networkRequest.resourceType];
|
|
802
|
+
const {iconName, color} = PanelUtils.iconDataForResourceType(resourceType);
|
|
803
|
+
|
|
804
|
+
return html`
|
|
805
|
+
<div class="network-request-preview">
|
|
806
|
+
<div class="network-request-header">
|
|
807
|
+
<div class="network-request-icon">
|
|
808
|
+
${
|
|
809
|
+
resourceType.isImage() ? html`<img src=${networkRequest.imageUrl ?? networkRequest.url} alt=${filename} />` :
|
|
810
|
+
html`<devtools-icon name=${iconName} style=${Lit.Directives.styleMap({
|
|
811
|
+
color: color ?? ''
|
|
812
|
+
})}></devtools-icon>`}
|
|
813
|
+
</div>
|
|
814
|
+
<div class="network-request-details">
|
|
815
|
+
<div class="network-request-name" title=${networkRequest.url}>${filename}</div>
|
|
816
|
+
<div class="network-request-size">${size}</div>
|
|
817
|
+
</div>
|
|
818
|
+
</div>
|
|
819
|
+
</div>
|
|
820
|
+
`;
|
|
821
|
+
}
|
|
822
|
+
|
|
753
823
|
async function makeDomTreeWidget(widgetData: DomTreeAiWidget): Promise<WidgetMakerResponse|null> {
|
|
754
824
|
const root = widgetData.data.root;
|
|
755
825
|
if (!(root instanceof SDK.DOMModel.DOMNodeSnapshot)) {
|
|
@@ -768,12 +838,17 @@ async function makeDomTreeWidget(widgetData: DomTreeAiWidget): Promise<WidgetMak
|
|
|
768
838
|
wrap: true,
|
|
769
839
|
});
|
|
770
840
|
|
|
841
|
+
const networkRequest = widgetData.data.networkRequest;
|
|
842
|
+
|
|
771
843
|
// clang-format off
|
|
772
|
-
const
|
|
844
|
+
const renderedWidget = html`
|
|
845
|
+
${networkRequest ? renderNetworkRequestPreview(networkRequest) : Lit.nothing}
|
|
846
|
+
<devtools-widget class="dom-tree-widget" .widgetConfig=${widgetConfig}></devtools-widget>
|
|
847
|
+
`;
|
|
773
848
|
// clang-format on
|
|
774
849
|
|
|
775
850
|
return {
|
|
776
|
-
renderedWidget
|
|
851
|
+
renderedWidget,
|
|
777
852
|
revealable: new SDK.DOMModel.DeferredDOMNode(root.domModel().target(), root.backendNodeId()),
|
|
778
853
|
};
|
|
779
854
|
}
|
|
@@ -802,14 +877,27 @@ async function renderWidgets(
|
|
|
802
877
|
}
|
|
803
878
|
const ui = await Promise.all(widgets.map(async widgetData => {
|
|
804
879
|
let response: WidgetMakerResponse|null = null;
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
880
|
+
switch (widgetData.name) {
|
|
881
|
+
case 'COMPUTED_STYLES':
|
|
882
|
+
response = await makeComputedStyleWidget(widgetData);
|
|
883
|
+
break;
|
|
884
|
+
case 'CORE_VITALS':
|
|
885
|
+
response = await makeCoreVitalsWidget(widgetData);
|
|
886
|
+
break;
|
|
887
|
+
case 'STYLE_PROPERTIES':
|
|
888
|
+
response = await makeStylePropertiesWidget(widgetData);
|
|
889
|
+
break;
|
|
890
|
+
case 'DOM_TREE':
|
|
891
|
+
response = await makeDomTreeWidget(widgetData);
|
|
892
|
+
break;
|
|
893
|
+
case 'PERFORMANCE_TRACE':
|
|
894
|
+
response = await makePerformanceTraceWidget(widgetData);
|
|
895
|
+
break;
|
|
896
|
+
case 'LCP_BREAKDOWN':
|
|
897
|
+
response = await makeLcpBreakdownWidget(widgetData);
|
|
898
|
+
break;
|
|
899
|
+
default:
|
|
900
|
+
Platform.assertNever(widgetData, 'Unknown AiWidget name');
|
|
813
901
|
}
|
|
814
902
|
return renderWidgetResponse(response);
|
|
815
903
|
}));
|
|
@@ -7,6 +7,7 @@ import '../../../ui/components/spinners/spinners.js';
|
|
|
7
7
|
import * as Host from '../../../core/host/host.js';
|
|
8
8
|
import * as i18n from '../../../core/i18n/i18n.js';
|
|
9
9
|
import type * as Platform from '../../../core/platform/platform.js';
|
|
10
|
+
import * as Root from '../../../core/root/root.js';
|
|
10
11
|
import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
|
|
11
12
|
import * as Buttons from '../../../ui/components/buttons/buttons.js';
|
|
12
13
|
import type {MarkdownLitRenderer} from '../../../ui/components/markdown_view/MarkdownView.js';
|
|
@@ -17,6 +18,7 @@ import {PatchWidget} from '../PatchWidget.js';
|
|
|
17
18
|
import {ChatInput} from './ChatInput.js';
|
|
18
19
|
import {ChatMessage, type Message, type ModelChatMessage} from './ChatMessage.js';
|
|
19
20
|
import chatViewStyles from './chatView.css.js';
|
|
21
|
+
import {ExportForAgentsDialog} from './ExportForAgentsDialog.js';
|
|
20
22
|
|
|
21
23
|
export {ChatInput, type ImageInputData} from './ChatInput.js';
|
|
22
24
|
|
|
@@ -63,6 +65,8 @@ export interface Props {
|
|
|
63
65
|
onCopyResponseClick: (message: ModelChatMessage) => void;
|
|
64
66
|
onContextRemoved: (() => void)|null;
|
|
65
67
|
onContextAdd: (() => void)|null;
|
|
68
|
+
conversationMarkdown: string;
|
|
69
|
+
onExportConversation: (() => void)|null;
|
|
66
70
|
changeManager: AiAssistanceModel.ChangeManager.ChangeManager;
|
|
67
71
|
inspectElementToggled: boolean;
|
|
68
72
|
messages: Message[];
|
|
@@ -81,6 +85,7 @@ export interface Props {
|
|
|
81
85
|
disclaimerText: Platform.UIString.LocalizedString;
|
|
82
86
|
uploadImageInputEnabled?: boolean;
|
|
83
87
|
markdownRenderer: MarkdownLitRenderer;
|
|
88
|
+
generateConversationSummary: (markdown: string) => Promise<string>;
|
|
84
89
|
walkthrough: {
|
|
85
90
|
onOpen: (message: ModelChatMessage) => void,
|
|
86
91
|
onToggle: (isOpen: boolean) => void,
|
|
@@ -94,6 +99,7 @@ interface ChatWidgetInput extends Props {
|
|
|
94
99
|
handleScroll: (ev: Event) => void;
|
|
95
100
|
handleSuggestionClick: (title: string) => void;
|
|
96
101
|
handleMessageContainerRef: (el: Element|undefined) => void;
|
|
102
|
+
exportForAgentsClick: () => void;
|
|
97
103
|
}
|
|
98
104
|
|
|
99
105
|
const DEFAULT_VIEW: View = (input, output, target) => {
|
|
@@ -107,6 +113,9 @@ const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
107
113
|
sticky: !input.isReadOnly,
|
|
108
114
|
});
|
|
109
115
|
|
|
116
|
+
const hasAiV2Flag = Boolean(Root.Runtime.hostConfig.devToolsAiAssistanceV2?.enabled);
|
|
117
|
+
const shouldShowExportToAgent = !input.isLoading && input.messages.length > 0 && hasAiV2Flag;
|
|
118
|
+
|
|
110
119
|
// clang-format off
|
|
111
120
|
render(html`
|
|
112
121
|
<style>${chatViewStyles}</style>
|
|
@@ -130,6 +139,15 @@ const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
130
139
|
}
|
|
131
140
|
})
|
|
132
141
|
)}
|
|
142
|
+
${shouldShowExportToAgent ? html`
|
|
143
|
+
<devtools-button
|
|
144
|
+
class="export-for-agents-button"
|
|
145
|
+
.jslogContext=${'ai-export-for-agents'}
|
|
146
|
+
.variant=${Buttons.Button.Variant.TEXT}
|
|
147
|
+
.iconName=${'copy'}
|
|
148
|
+
@click=${input.exportForAgentsClick}
|
|
149
|
+
>Export for agents</devtools-button>
|
|
150
|
+
` : nothing}
|
|
133
151
|
${input.isLoading ? nothing : widget(PatchWidget, {
|
|
134
152
|
changeSummary: input.changeSummary ?? '',
|
|
135
153
|
changeManager: input.changeManager,
|
|
@@ -233,6 +251,7 @@ export class ChatView extends HTMLElement {
|
|
|
233
251
|
*/
|
|
234
252
|
#isProgrammaticScroll = false;
|
|
235
253
|
#view: View;
|
|
254
|
+
#cachedSummary: {markdown: string, summary: string}|null = null;
|
|
236
255
|
|
|
237
256
|
constructor(props: Props, view = DEFAULT_VIEW) {
|
|
238
257
|
super();
|
|
@@ -346,6 +365,30 @@ export class ChatView extends HTMLElement {
|
|
|
346
365
|
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceDynamicSuggestionClicked);
|
|
347
366
|
};
|
|
348
367
|
|
|
368
|
+
async #getSummary(): Promise<string> {
|
|
369
|
+
if (this.#cachedSummary?.markdown === this.#props.conversationMarkdown) {
|
|
370
|
+
return this.#cachedSummary.summary;
|
|
371
|
+
}
|
|
372
|
+
try {
|
|
373
|
+
const summary = await this.#props.generateConversationSummary(this.#props.conversationMarkdown);
|
|
374
|
+
this.#cachedSummary = {markdown: this.#props.conversationMarkdown, summary};
|
|
375
|
+
return summary;
|
|
376
|
+
} catch (err) {
|
|
377
|
+
console.error(err);
|
|
378
|
+
return 'Failed to generate summary.';
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
async #exportForAgentsClick(): Promise<void> {
|
|
383
|
+
const summaryPromise = this.#getSummary();
|
|
384
|
+
|
|
385
|
+
void ExportForAgentsDialog.show({
|
|
386
|
+
promptText: summaryPromise,
|
|
387
|
+
markdownText: this.#props.conversationMarkdown,
|
|
388
|
+
onConversationSaveAs: this.#props.onExportConversation ?? (async () => {}),
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
349
392
|
#render(): void {
|
|
350
393
|
this.#view(
|
|
351
394
|
{
|
|
@@ -353,6 +396,7 @@ export class ChatView extends HTMLElement {
|
|
|
353
396
|
handleScroll: this.#handleScroll,
|
|
354
397
|
handleSuggestionClick: this.#handleSuggestionClick,
|
|
355
398
|
handleMessageContainerRef: this.#handleMessageContainerRef,
|
|
399
|
+
exportForAgentsClick: this.#exportForAgentsClick.bind(this),
|
|
356
400
|
},
|
|
357
401
|
this.#output, this.#shadow);
|
|
358
402
|
}
|
|
@@ -0,0 +1,240 @@
|
|
|
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 Host from '../../../core/host/host.js';
|
|
6
|
+
import * as i18n from '../../../core/i18n/i18n.js';
|
|
7
|
+
import * as Buttons from '../../../ui/components/buttons/buttons.js';
|
|
8
|
+
import * as Snackbars from '../../../ui/components/snackbars/snackbars.js';
|
|
9
|
+
import * as UI from '../../../ui/legacy/legacy.js';
|
|
10
|
+
import * as Lit from '../../../ui/lit/lit.js';
|
|
11
|
+
|
|
12
|
+
import styles from './exportForAgentsDialog.css.js';
|
|
13
|
+
|
|
14
|
+
const {html, render} = Lit;
|
|
15
|
+
|
|
16
|
+
const UIStrings = {
|
|
17
|
+
/**
|
|
18
|
+
* @description Title for the export for agents dialog.
|
|
19
|
+
*/
|
|
20
|
+
exportForAgents: 'Export for agents',
|
|
21
|
+
/**
|
|
22
|
+
* @description Button text for copying to clipboard.
|
|
23
|
+
*/
|
|
24
|
+
copyToClipboard: 'Copy to clipboard',
|
|
25
|
+
/**
|
|
26
|
+
* @description Text displayed in a toast to indicate that the content was copied to the clipboard.
|
|
27
|
+
*/
|
|
28
|
+
copiedToClipboard: 'Copied to clipboard',
|
|
29
|
+
/**
|
|
30
|
+
* @description Label for the 'as prompt' radio button in the export for agents dialog.
|
|
31
|
+
*/
|
|
32
|
+
asPrompt: 'As prompt',
|
|
33
|
+
/**
|
|
34
|
+
* @description Label for the 'as markdown' radio button in the export for agents dialog.
|
|
35
|
+
*/
|
|
36
|
+
asMarkdown: 'As markdown',
|
|
37
|
+
/**
|
|
38
|
+
* @description Button text for saving content as a markdown file.
|
|
39
|
+
*/
|
|
40
|
+
saveAsMarkdown: 'Save as…',
|
|
41
|
+
/**
|
|
42
|
+
* @description Text displayed while the summary is being generated.
|
|
43
|
+
*/
|
|
44
|
+
generatingSummary: 'Generating summary…',
|
|
45
|
+
/**
|
|
46
|
+
* @description Disclaimer text for the export for agents dialog.
|
|
47
|
+
*/
|
|
48
|
+
disclaimer:
|
|
49
|
+
'This is an experimental AI feature and won’t always get it right. Double check this text before pasting into another tool.',
|
|
50
|
+
} as const;
|
|
51
|
+
|
|
52
|
+
const str_ = i18n.i18n.registerUIStrings('panels/ai_assistance/components/ExportForAgentsDialog.ts', UIStrings);
|
|
53
|
+
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
54
|
+
|
|
55
|
+
export const enum StateType {
|
|
56
|
+
PROMPT = 'prompt',
|
|
57
|
+
CONVERSATION = 'conversation',
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface State {
|
|
61
|
+
activeType: StateType;
|
|
62
|
+
promptText: string;
|
|
63
|
+
conversationText: string;
|
|
64
|
+
isPromptLoading: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
interface ViewInput {
|
|
68
|
+
onButtonClick: (event: Event) => void;
|
|
69
|
+
state: State;
|
|
70
|
+
onStateChange: (stateType: StateType) => void;
|
|
71
|
+
jslogContext: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;
|
|
75
|
+
|
|
76
|
+
export const DEFAULT_VIEW: View = (input, _output, target): void => {
|
|
77
|
+
const isPrompt = input.state.activeType === StateType.PROMPT;
|
|
78
|
+
const buttonText = isPrompt ? i18nString(UIStrings.copyToClipboard) : i18nString(UIStrings.saveAsMarkdown);
|
|
79
|
+
const exportText = isPrompt && input.state.isPromptLoading ?
|
|
80
|
+
i18nString(UIStrings.generatingSummary) :
|
|
81
|
+
(isPrompt ? input.state.promptText : input.state.conversationText);
|
|
82
|
+
// clang-format off
|
|
83
|
+
render(html`
|
|
84
|
+
<style>${styles}</style>
|
|
85
|
+
<div class="export-for-agents-dialog">
|
|
86
|
+
<header>
|
|
87
|
+
<h2 tabindex="-1">
|
|
88
|
+
${i18nString(UIStrings.exportForAgents)}
|
|
89
|
+
</h2>
|
|
90
|
+
</header>
|
|
91
|
+
<div class="state-selection">
|
|
92
|
+
<label>
|
|
93
|
+
<input
|
|
94
|
+
type="radio"
|
|
95
|
+
value="prompt"
|
|
96
|
+
name="export-state"
|
|
97
|
+
.checked=${isPrompt}
|
|
98
|
+
@change=${() => input.onStateChange(StateType.PROMPT)}
|
|
99
|
+
>
|
|
100
|
+
${i18nString(UIStrings.asPrompt)}
|
|
101
|
+
</label>
|
|
102
|
+
<label>
|
|
103
|
+
<input
|
|
104
|
+
type="radio"
|
|
105
|
+
value="conversation"
|
|
106
|
+
name="export-state"
|
|
107
|
+
.checked=${!isPrompt}
|
|
108
|
+
@change=${() => input.onStateChange(StateType.CONVERSATION)}
|
|
109
|
+
>
|
|
110
|
+
${i18nString(UIStrings.asMarkdown)}
|
|
111
|
+
</label>
|
|
112
|
+
</div>
|
|
113
|
+
<main>
|
|
114
|
+
<textarea readonly .value=${exportText}></textarea>
|
|
115
|
+
</main>
|
|
116
|
+
${isPrompt ? html`<div class="disclaimer">${i18nString(UIStrings.disclaimer)}</div>` : Lit.nothing}
|
|
117
|
+
<footer>
|
|
118
|
+
<div class="right-buttons">
|
|
119
|
+
<devtools-button
|
|
120
|
+
@click=${input.onButtonClick}
|
|
121
|
+
.jslogContext=${input.jslogContext}
|
|
122
|
+
.variant=${Buttons.Button.Variant.PRIMARY}
|
|
123
|
+
.disabled=${isPrompt && input.state.isPromptLoading}
|
|
124
|
+
>
|
|
125
|
+
${buttonText}
|
|
126
|
+
</devtools-button>
|
|
127
|
+
</div>
|
|
128
|
+
</footer>
|
|
129
|
+
</div>
|
|
130
|
+
`, target);
|
|
131
|
+
// clang-format on
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
export class ExportForAgentsDialog extends UI.Widget.VBox {
|
|
135
|
+
readonly #view: View;
|
|
136
|
+
readonly #dialog: UI.Dialog.Dialog;
|
|
137
|
+
#state: State;
|
|
138
|
+
#onConversationSaveAs: () => void;
|
|
139
|
+
|
|
140
|
+
constructor(
|
|
141
|
+
options: {
|
|
142
|
+
dialog: UI.Dialog.Dialog,
|
|
143
|
+
promptText: string|Promise<string>,
|
|
144
|
+
markdownText: string,
|
|
145
|
+
onConversationSaveAs: () => void,
|
|
146
|
+
},
|
|
147
|
+
view: View = DEFAULT_VIEW) {
|
|
148
|
+
super();
|
|
149
|
+
this.#dialog = options.dialog;
|
|
150
|
+
this.#state = {
|
|
151
|
+
activeType: StateType.PROMPT,
|
|
152
|
+
promptText: typeof options.promptText === 'string' ? options.promptText : '',
|
|
153
|
+
conversationText: options.markdownText,
|
|
154
|
+
isPromptLoading: typeof options.promptText !== 'string',
|
|
155
|
+
};
|
|
156
|
+
this.#onConversationSaveAs = options.onConversationSaveAs;
|
|
157
|
+
this.#view = view;
|
|
158
|
+
|
|
159
|
+
if (options.promptText instanceof Promise) {
|
|
160
|
+
void options.promptText.then(promptText => {
|
|
161
|
+
this.#state.promptText = promptText;
|
|
162
|
+
this.#state.isPromptLoading = false;
|
|
163
|
+
this.requestUpdate();
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
this.requestUpdate();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
#onStateChange = (newState: StateType): void => {
|
|
171
|
+
this.#state.activeType = newState;
|
|
172
|
+
this.requestUpdate();
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
override performUpdate(): void {
|
|
176
|
+
let onButtonClick: (event: Event) => void;
|
|
177
|
+
let jslogContext = '';
|
|
178
|
+
|
|
179
|
+
switch (this.#state.activeType) {
|
|
180
|
+
case StateType.PROMPT:
|
|
181
|
+
jslogContext = 'ai-export-for-agents.copy-to-clipboard';
|
|
182
|
+
onButtonClick = (event: Event): void => {
|
|
183
|
+
event.preventDefault();
|
|
184
|
+
Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(this.#state.promptText);
|
|
185
|
+
Snackbars.Snackbar.Snackbar.show({
|
|
186
|
+
message: i18nString(UIStrings.copiedToClipboard),
|
|
187
|
+
});
|
|
188
|
+
this.#dialog.hide();
|
|
189
|
+
};
|
|
190
|
+
break;
|
|
191
|
+
case StateType.CONVERSATION:
|
|
192
|
+
jslogContext = 'ai-export-for-agents.save-as-markdown';
|
|
193
|
+
onButtonClick = (): void => {
|
|
194
|
+
this.#dialog.hide();
|
|
195
|
+
this.#onConversationSaveAs();
|
|
196
|
+
};
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const viewInput = {
|
|
201
|
+
onButtonClick,
|
|
202
|
+
state: this.#state,
|
|
203
|
+
onStateChange: this.#onStateChange,
|
|
204
|
+
jslogContext,
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
this.#view(viewInput, undefined, this.contentElement);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
static show({
|
|
211
|
+
promptText,
|
|
212
|
+
markdownText,
|
|
213
|
+
onConversationSaveAs,
|
|
214
|
+
}: {
|
|
215
|
+
promptText: string|Promise<string>,
|
|
216
|
+
markdownText: string,
|
|
217
|
+
onConversationSaveAs: () => void,
|
|
218
|
+
}): void {
|
|
219
|
+
const dialog = new UI.Dialog.Dialog();
|
|
220
|
+
dialog.setAriaLabel(i18nString(UIStrings.exportForAgents));
|
|
221
|
+
|
|
222
|
+
dialog.setOutsideClickCallback(ev => {
|
|
223
|
+
ev.consume(true);
|
|
224
|
+
dialog.hide();
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
dialog.addCloseButton();
|
|
228
|
+
dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.MEASURE_CONTENT);
|
|
229
|
+
dialog.setDimmed(true);
|
|
230
|
+
|
|
231
|
+
const exportDialog = new ExportForAgentsDialog({dialog, promptText, markdownText, onConversationSaveAs});
|
|
232
|
+
exportDialog.show(dialog.contentElement);
|
|
233
|
+
// Because the Dialog sets its height based off its content, we need
|
|
234
|
+
// the Export Dialog widget to have rendered fully before we show
|
|
235
|
+
// the dialog with its contents.
|
|
236
|
+
void exportDialog.updateComplete.then(() => {
|
|
237
|
+
dialog.show();
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
@@ -13,7 +13,7 @@ import * as PanelsCommon from '../../common/common.js';
|
|
|
13
13
|
import {MarkdownRendererWithCodeBlock} from './MarkdownRendererWithCodeBlock.js';
|
|
14
14
|
|
|
15
15
|
const {html} = Lit.StaticHtml;
|
|
16
|
-
const {
|
|
16
|
+
const {until} = Lit.Directives;
|
|
17
17
|
|
|
18
18
|
export class PerformanceAgentMarkdownRenderer extends MarkdownRendererWithCodeBlock {
|
|
19
19
|
constructor(
|
|
@@ -27,16 +27,8 @@ export class PerformanceAgentMarkdownRenderer extends MarkdownRendererWithCodeBl
|
|
|
27
27
|
if (token.href.startsWith('#node-')) {
|
|
28
28
|
const nodeId = Number(token.href.replace('#node-', '')) as Protocol.DOM.BackendNodeId;
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if (!templateRef.value || !node) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
templateRef.value.textContent = '';
|
|
37
|
-
templateRef.value.append(node);
|
|
38
|
-
});
|
|
39
|
-
return html`<span ${ref(templateRef)}>${token.text}</span>`;
|
|
30
|
+
return html`<span>${
|
|
31
|
+
until(this.#linkifyNode(nodeId, token.text).then(node => node || token.text), token.text)}</span>`;
|
|
40
32
|
}
|
|
41
33
|
|
|
42
34
|
const event = this.lookupEvent(token.href.slice(1) as Trace.Types.File.SerializableKey);
|
|
@@ -65,7 +57,7 @@ export class PerformanceAgentMarkdownRenderer extends MarkdownRendererWithCodeBl
|
|
|
65
57
|
// Taken from front_end/panels/timeline/components/insights/NodeLink.ts
|
|
66
58
|
// Would be nice to move the above component to somewhere that allows the AI
|
|
67
59
|
// Assistance panel to also use it.
|
|
68
|
-
async #linkifyNode(backendNodeId: Protocol.DOM.BackendNodeId, label: string): Promise<
|
|
60
|
+
async #linkifyNode(backendNodeId: Protocol.DOM.BackendNodeId, label: string): Promise<Lit.LitTemplate|undefined> {
|
|
69
61
|
if (backendNodeId === undefined) {
|
|
70
62
|
return;
|
|
71
63
|
}
|