chrome-devtools-frontend 1.0.1605219 → 1.0.1606789
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/front_end/Images/src/dots-circle.svg +10 -0
- package/front_end/core/host/UserMetrics.ts +0 -1
- package/front_end/core/platform/api/HostRuntime.ts +9 -6
- package/front_end/core/platform/browser/HostRuntime.ts +2 -2
- package/front_end/core/platform/node/HostRuntime.ts +7 -7
- package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
- package/front_end/core/root/ExperimentNames.ts +0 -1
- package/front_end/core/sdk/CrashReportContextModel.ts +28 -0
- package/front_end/core/sdk/sdk.ts +2 -2
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.ts +3 -3
- package/front_end/entrypoints/heap_snapshot_worker/heap_snapshot_worker-entrypoint.ts +5 -4
- package/front_end/entrypoints/main/MainImpl.ts +0 -101
- package/front_end/models/ai_assistance/ChangeManager.ts +6 -6
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +112 -5
- package/front_end/models/ai_assistance/agents/AiAgent.ts +9 -25
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +2 -2
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +6 -3
- package/front_end/models/ai_assistance/agents/ConversationSummaryAgent.ts +10 -9
- package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +313 -0
- package/front_end/models/ai_assistance/agents/FileAgent.ts +15 -1
- package/front_end/models/ai_assistance/agents/NetworkAgent.ts +15 -1
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +19 -8
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +35 -278
- package/front_end/models/ai_assistance/ai_assistance.ts +0 -2
- package/front_end/models/javascript_metadata/NativeFunctions.js +1 -5
- package/front_end/models/web_mcp/WebMCPModel.ts +187 -0
- package/front_end/models/web_mcp/web_mcp.ts +9 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +8 -1
- package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
- package/front_end/panels/ai_assistance/components/AccessibilityAgentMarkdownRenderer.ts +88 -0
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +149 -49
- package/front_end/panels/ai_assistance/components/ChatView.ts +11 -15
- package/front_end/panels/ai_assistance/components/ExportForAgentsDialog.ts +13 -4
- package/front_end/panels/ai_assistance/components/chatMessage.css +53 -1
- package/front_end/panels/ai_assistance/components/chatView.css +0 -10
- package/front_end/panels/ai_assistance/components/exportForAgentsDialog.css +13 -0
- package/front_end/panels/application/WebMCPView.ts +471 -25
- package/front_end/panels/application/webMCPView.css +53 -2
- package/front_end/panels/elements/ElementsTreeElement.ts +1 -1
- package/front_end/panels/elements/ElementsTreeOutline.ts +87 -0
- package/front_end/panels/elements/StylePropertiesSection.ts +0 -4
- package/front_end/panels/elements/elements.ts +3 -0
- package/front_end/panels/elements/elementsTreeOutline.css +21 -0
- package/front_end/panels/elements/stylePropertiesTreeOutline.css +7 -0
- package/front_end/panels/lighthouse/LighthousePanel.ts +7 -1
- package/front_end/panels/lighthouse/LighthouseProtocolService.ts +25 -2
- package/front_end/panels/profiler/HeapDetachedElementsView.ts +0 -4
- package/front_end/panels/profiler/HeapProfileView.ts +0 -4
- package/front_end/panels/profiler/HeapProfilerPanel.ts +4 -13
- package/front_end/panels/profiler/HeapSnapshotProxy.ts +2 -6
- package/front_end/panels/profiler/HeapSnapshotView.ts +19 -32
- package/front_end/panels/profiler/ProfileHeader.ts +1 -15
- package/front_end/panels/profiler/ProfileTypeRegistry.ts +4 -12
- package/front_end/panels/profiler/ProfileView.ts +1 -6
- package/front_end/panels/profiler/ProfilesPanel.ts +60 -7
- package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +27 -22
- package/front_end/panels/timeline/EventsTimelineTreeView.ts +1 -1
- package/front_end/panels/timeline/ThirdPartyTreeView.ts +2 -2
- package/front_end/panels/timeline/TimelinePanel.ts +11 -153
- package/front_end/panels/timeline/TimelineTreeView.ts +147 -48
- package/front_end/panels/timeline/TimelineUIUtils.ts +2 -1
- package/front_end/panels/timeline/components/CWVMetrics.ts +18 -2
- package/front_end/panels/timeline/timeline-meta.ts +11 -0
- package/front_end/panels/timeline/utils/Helpers.ts +5 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/markdown_view/CodeBlock.ts +47 -1
- package/front_end/ui/components/markdown_view/codeBlock.css +8 -0
- package/front_end/ui/legacy/FilterBar.ts +2 -0
- package/front_end/ui/legacy/SplitWidget.ts +33 -27
- package/front_end/ui/legacy/TabbedPane.ts +123 -3
- package/front_end/ui/legacy/Widget.ts +95 -48
- package/front_end/ui/legacy/components/source_frame/JSONView.ts +1 -1
- package/front_end/ui/legacy/components/utils/jsUtils.css +2 -0
- package/front_end/ui/legacy/infobar.css +1 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +6 -1
- package/package.json +1 -1
- package/front_end/core/sdk/WebMCPModel.ts +0 -104
- package/front_end/models/ai_assistance/ConversationHandler.ts +0 -325
|
@@ -0,0 +1,88 @@
|
|
|
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 SDK from '../../../core/sdk/sdk.js';
|
|
6
|
+
import type * as Protocol from '../../../generated/protocol.js';
|
|
7
|
+
import type * as Marked from '../../../third_party/marked/marked.js';
|
|
8
|
+
import * as Lit from '../../../ui/lit/lit.js';
|
|
9
|
+
import * as PanelsCommon from '../../common/common.js';
|
|
10
|
+
|
|
11
|
+
import {MarkdownRendererWithCodeBlock} from './MarkdownRendererWithCodeBlock.js';
|
|
12
|
+
|
|
13
|
+
const {html} = Lit.StaticHtml;
|
|
14
|
+
const {until} = Lit.Directives;
|
|
15
|
+
|
|
16
|
+
export class AccessibilityAgentMarkdownRenderer extends MarkdownRendererWithCodeBlock {
|
|
17
|
+
constructor(
|
|
18
|
+
private mainFrameId = '',
|
|
19
|
+
) {
|
|
20
|
+
super();
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
override templateForToken(token: Marked.Marked.MarkedToken): Lit.LitTemplate|null {
|
|
24
|
+
if (token.type === 'link' && token.href.startsWith('#')) {
|
|
25
|
+
if (token.href.startsWith('#path-')) {
|
|
26
|
+
const path = token.href.replace('#path-', '');
|
|
27
|
+
return html`<span>${
|
|
28
|
+
until(this.#linkifyPath(path, token.text).then(node => node || token.text), token.text)}</span>`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let nodeId = undefined;
|
|
32
|
+
if (token.href.startsWith('#node-')) {
|
|
33
|
+
nodeId = Number(token.href.replace('#node-', '')) as Protocol.DOM.BackendNodeId;
|
|
34
|
+
} else if (token.href.startsWith('#')) {
|
|
35
|
+
nodeId = Number(token.href.replace('#', '')) as Protocol.DOM.BackendNodeId;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (nodeId) {
|
|
39
|
+
return html`<span>${
|
|
40
|
+
until(this.#linkifyNode(nodeId, token.text).then(node => node || token.text), token.text)}</span>`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return super.templateForToken(token);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async #linkifyNode(backendNodeId: Protocol.DOM.BackendNodeId, label: string): Promise<Lit.LitTemplate|undefined> {
|
|
48
|
+
if (backendNodeId === undefined) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
|
|
53
|
+
const domModel = target?.model(SDK.DOMModel.DOMModel);
|
|
54
|
+
if (!domModel) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
const domNodesMap = await domModel.pushNodesByBackendIdsToFrontend(new Set([backendNodeId]));
|
|
58
|
+
const node = domNodesMap?.get(backendNodeId);
|
|
59
|
+
if (!node) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (node.frameId() !== this.mainFrameId) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const linkedNode = PanelsCommon.DOMLinkifier.Linkifier.instance().linkify(node, {textContent: label});
|
|
68
|
+
return linkedNode;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async #linkifyPath(path: string, label: string): Promise<Lit.LitTemplate|undefined> {
|
|
72
|
+
const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
|
|
73
|
+
const domModel = target?.model(SDK.DOMModel.DOMModel);
|
|
74
|
+
if (!domModel) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
const nodeId = await domModel.pushNodeByPathToFrontend(path);
|
|
78
|
+
if (!nodeId) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const node = domModel.nodeForId(nodeId);
|
|
82
|
+
if (!node) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const linkedNode = PanelsCommon.DOMLinkifier.Linkifier.instance().linkify(node, {textContent: label});
|
|
86
|
+
return linkedNode;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -13,8 +13,9 @@ 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, LcpBreakdownAiWidget,
|
|
17
|
-
StylePropertiesAiWidget,
|
|
16
|
+
AiWidget, BottomUpTreeAiWidget, ComputedStyleAiWidget, CoreVitalsAiWidget, DomTreeAiWidget, LcpBreakdownAiWidget,
|
|
17
|
+
PerformanceTraceAiWidget, StylePropertiesAiWidget,
|
|
18
|
+
TimelineRangeSummaryAiWidget} from '../../../models/ai_assistance/agents/AiAgent.js';
|
|
18
19
|
import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
|
|
19
20
|
import * as ComputedStyle from '../../../models/computed_style/computed_style.js';
|
|
20
21
|
import * as Trace from '../../../models/trace/trace.js';
|
|
@@ -44,6 +45,7 @@ const {widget} = UI.Widget;
|
|
|
44
45
|
|
|
45
46
|
const REPORT_URL = 'https://crbug.com/364805393' as Platform.DevToolsPath.UrlString;
|
|
46
47
|
const SCROLL_ROUNDING_OFFSET = 1;
|
|
48
|
+
const MAX_NUM_LINES_IN_CODEBLOCK = 11;
|
|
47
49
|
|
|
48
50
|
/*
|
|
49
51
|
* Strings that don't need to be translated at this time.
|
|
@@ -193,7 +195,15 @@ const UIStringsNotTranslate = {
|
|
|
193
195
|
/**
|
|
194
196
|
* @description Title for the performance summary widget.
|
|
195
197
|
*/
|
|
196
|
-
performanceSummary: 'Performance summary'
|
|
198
|
+
performanceSummary: 'Performance summary',
|
|
199
|
+
/**
|
|
200
|
+
* @description The title of the button that allows exporting the conversation for agents.
|
|
201
|
+
*/
|
|
202
|
+
exportForAgents: 'Copy for your coding agent',
|
|
203
|
+
/**
|
|
204
|
+
* @description Title for the bottom up thread activity widget.
|
|
205
|
+
*/
|
|
206
|
+
bottomUpTree: 'Bottom-up thread activity',
|
|
197
207
|
} as const;
|
|
198
208
|
|
|
199
209
|
export interface Step {
|
|
@@ -260,6 +270,7 @@ export interface RatingViewInput {
|
|
|
260
270
|
export interface ActionViewInput {
|
|
261
271
|
onReportClick: () => void;
|
|
262
272
|
onCopyResponseClick: () => void;
|
|
273
|
+
onExportClick?: () => void;
|
|
263
274
|
showActions: boolean;
|
|
264
275
|
}
|
|
265
276
|
|
|
@@ -293,11 +304,14 @@ export interface MessageInput {
|
|
|
293
304
|
isLoading: boolean;
|
|
294
305
|
isReadOnly: boolean;
|
|
295
306
|
isLastMessage: boolean;
|
|
307
|
+
isFirstMessage: boolean;
|
|
296
308
|
canShowFeedbackForm: boolean;
|
|
297
309
|
markdownRenderer: MarkdownLitRenderer;
|
|
298
310
|
onSuggestionClick: (suggestion: string) => void;
|
|
299
311
|
onFeedbackSubmit: (rpcId: Host.AidaClient.RpcGlobalId, rate: Host.AidaClient.Rating, feedback?: string) => void;
|
|
300
312
|
onCopyResponseClick: (message: ModelChatMessage) => void;
|
|
313
|
+
onExportClick?: () => void;
|
|
314
|
+
changeSummary?: string;
|
|
301
315
|
walkthrough: {
|
|
302
316
|
onOpen: (message: ModelChatMessage) => void,
|
|
303
317
|
isExpanded: boolean,
|
|
@@ -309,22 +323,37 @@ export interface MessageInput {
|
|
|
309
323
|
}
|
|
310
324
|
|
|
311
325
|
export const DEFAULT_VIEW = (input: ChatMessageViewInput, output: ViewOutput, target: HTMLElement): void => {
|
|
326
|
+
const hasAiV2 = Boolean(Root.Runtime.hostConfig.devToolsAiAssistanceV2?.enabled);
|
|
312
327
|
const message = input.message;
|
|
328
|
+
|
|
313
329
|
if (message.entity === ChatMessageEntity.USER) {
|
|
314
330
|
const imageInput = message.imageInput && 'inlineData' in message.imageInput ?
|
|
315
331
|
renderImageChatMessage(message.imageInput.inlineData) :
|
|
316
332
|
Lit.nothing;
|
|
333
|
+
|
|
334
|
+
const messageClasses = Lit.Directives.classMap({
|
|
335
|
+
'chat-message': true,
|
|
336
|
+
query: true,
|
|
337
|
+
'is-last-message': input.isLastMessage,
|
|
338
|
+
'is-first-message': input.isFirstMessage,
|
|
339
|
+
'ai-v2': hasAiV2,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
const userQueryWrapperClasses = Lit.Directives.classMap({
|
|
343
|
+
// Don't need to style at all unless we are on the V2 flag.
|
|
344
|
+
// Once we ship this can be removed entirely.
|
|
345
|
+
'user-query-wrapper': hasAiV2
|
|
346
|
+
});
|
|
317
347
|
// clang-format off
|
|
318
348
|
Lit.render(html`
|
|
319
349
|
<style>${Input.textInputStyles}</style>
|
|
320
350
|
<style>${chatMessageStyles}</style>
|
|
321
|
-
<
|
|
322
|
-
class
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
</section>
|
|
351
|
+
<div class=${userQueryWrapperClasses}>
|
|
352
|
+
<section class=${messageClasses} jslog=${VisualLogging.section('question')}>
|
|
353
|
+
${imageInput}
|
|
354
|
+
<div class="message-content">${renderTextAsMarkdown(message.text, input.markdownRenderer)}</div>
|
|
355
|
+
</section>
|
|
356
|
+
</div>
|
|
328
357
|
`, target);
|
|
329
358
|
// clang-format on
|
|
330
359
|
return;
|
|
@@ -332,48 +361,62 @@ export const DEFAULT_VIEW = (input: ChatMessageViewInput, output: ViewOutput, ta
|
|
|
332
361
|
|
|
333
362
|
const steps = message.parts.filter(part => part.type === 'step').map(part => part.step);
|
|
334
363
|
const icon = AiAssistanceModel.AiUtils.getIconName();
|
|
335
|
-
const aiAssistanceV2 = Root.Runtime.hostConfig.devToolsAiAssistanceV2?.enabled;
|
|
336
364
|
|
|
365
|
+
const messageClasses = Lit.Directives.classMap({
|
|
366
|
+
'chat-message': true,
|
|
367
|
+
answer: true,
|
|
368
|
+
'is-last-message': input.isLastMessage,
|
|
369
|
+
'is-first-message': input.isFirstMessage,
|
|
370
|
+
'ai-v2': hasAiV2,
|
|
371
|
+
});
|
|
337
372
|
// clang-format off
|
|
338
373
|
Lit.render(html`
|
|
339
374
|
<style>${Input.textInputStyles}</style>
|
|
340
375
|
<style>${chatMessageStyles}</style>
|
|
341
|
-
<section
|
|
342
|
-
|
|
343
|
-
jslog=${VisualLogging.section('answer')}
|
|
344
|
-
>
|
|
345
|
-
${aiAssistanceV2 ? Lit.nothing : html`
|
|
376
|
+
<section class=${messageClasses} jslog=${VisualLogging.section('answer')}>
|
|
377
|
+
${hasAiV2 ? Lit.nothing : html`
|
|
346
378
|
<div class="message-info">
|
|
347
379
|
<devtools-icon name=${icon}></devtools-icon>
|
|
348
380
|
<div class="message-name">
|
|
349
381
|
<h2>${AiAssistanceModel.AiUtils.isGeminiBranding() ? lockedString(UIStringsNotTranslate.gemini) : lockedString(UIStringsNotTranslate.ai)}</h2>
|
|
350
382
|
</div>
|
|
351
383
|
</div>`}
|
|
352
|
-
${
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
384
|
+
${hasAiV2 ? renderWalkthroughUI(input, steps) : Lit.nothing}
|
|
385
|
+
<div class="answer-body-wrapper">
|
|
386
|
+
${Lit.Directives.repeat(
|
|
387
|
+
message.parts,
|
|
388
|
+
(_, index) => index,
|
|
389
|
+
(part, index) => {
|
|
390
|
+
const isLastPart = index === message.parts.length - 1;
|
|
391
|
+
if (part.type === 'answer') {
|
|
392
|
+
return html`<p>${renderTextAsMarkdown(part.text, input.markdownRenderer, { animate: !input.isReadOnly && input.isLoading && isLastPart && input.isLastMessage })}</p>`;
|
|
393
|
+
}
|
|
394
|
+
if (part.type === 'widget') {
|
|
395
|
+
return html`${Lit.Directives.until(renderWidgets(part.widgets, {wrapperClass: 'main-widgets-wrapper'}))}`;
|
|
396
|
+
}
|
|
397
|
+
if (!hasAiV2 && part.type === 'step') {
|
|
398
|
+
return renderStep({
|
|
399
|
+
step: part.step,
|
|
400
|
+
isLoading: input.isLoading,
|
|
401
|
+
markdownRenderer: input.markdownRenderer,
|
|
402
|
+
isLast: isLastPart,
|
|
403
|
+
});
|
|
404
|
+
}
|
|
405
|
+
return Lit.nothing;
|
|
406
|
+
},
|
|
407
|
+
)}
|
|
408
|
+
${renderError(message)}
|
|
409
|
+
${input.isLastMessage && hasAiV2 && !input.isLoading && input.changeSummary ? html`
|
|
410
|
+
<devtools-code-block
|
|
411
|
+
.code=${input.changeSummary}
|
|
412
|
+
.codeLang=${'css'}
|
|
413
|
+
.displayLimit=${MAX_NUM_LINES_IN_CODEBLOCK}
|
|
414
|
+
.displayNotice=${true}
|
|
415
|
+
class="ai-css-change"
|
|
416
|
+
></devtools-code-block>
|
|
417
|
+
` : Lit.nothing}
|
|
418
|
+
${input.showActions ? renderActions(input, output) : Lit.nothing}
|
|
419
|
+
</div>
|
|
377
420
|
</section>
|
|
378
421
|
`, target);
|
|
379
422
|
// clang-format on
|
|
@@ -439,6 +482,7 @@ function renderStepCode(step: Step): Lit.LitTemplate {
|
|
|
439
482
|
<devtools-code-block
|
|
440
483
|
.code=${step.code.trim()}
|
|
441
484
|
.codeLang=${'js'}
|
|
485
|
+
.displayLimit=${MAX_NUM_LINES_IN_CODEBLOCK}
|
|
442
486
|
.displayNotice=${!Boolean(step.output)}
|
|
443
487
|
.header=${codeHeadingText}
|
|
444
488
|
.showCopyButton=${true}
|
|
@@ -449,6 +493,7 @@ function renderStepCode(step: Step): Lit.LitTemplate {
|
|
|
449
493
|
<devtools-code-block
|
|
450
494
|
.code=${step.output}
|
|
451
495
|
.codeLang=${'js'}
|
|
496
|
+
.displayLimit=${MAX_NUM_LINES_IN_CODEBLOCK}
|
|
452
497
|
.displayNotice=${true}
|
|
453
498
|
.header=${lockedString(UIStringsNotTranslate.dataReturned)}
|
|
454
499
|
.showCopyButton=${false}
|
|
@@ -481,6 +526,7 @@ function renderStepDetails({
|
|
|
481
526
|
<devtools-code-block
|
|
482
527
|
.code=${contextDetail.text}
|
|
483
528
|
.codeLang=${contextDetail.codeLang || ''}
|
|
529
|
+
.displayLimit=${MAX_NUM_LINES_IN_CODEBLOCK}
|
|
484
530
|
.displayNotice=${false}
|
|
485
531
|
.header=${contextDetail.title}
|
|
486
532
|
.showCopyButton=${true}
|
|
@@ -739,10 +785,9 @@ async function makeComputedStyleWidget(widgetData: ComputedStyleAiWidget): Promi
|
|
|
739
785
|
};
|
|
740
786
|
}
|
|
741
787
|
|
|
742
|
-
async function
|
|
788
|
+
async function makeCoreWebVitalsWidget(widgetData: CoreVitalsAiWidget): Promise<WidgetMakerResponse|null> {
|
|
743
789
|
// clang-format off
|
|
744
|
-
const renderedWidget = html`<devtools-widget
|
|
745
|
-
class="core-vitals-widget" ${widget(TimelineComponents.CWVMetrics.CWVMetrics, {data: widgetData.data})}>
|
|
790
|
+
const renderedWidget = html`<devtools-widget class="core-vitals-widget" ${widget(TimelineComponents.CWVMetrics.CWVMetrics, {data: widgetData.data, skipBottomBorder: true})}>
|
|
746
791
|
</devtools-widget>`;
|
|
747
792
|
// clang-format on
|
|
748
793
|
|
|
@@ -802,6 +847,34 @@ async function makeLcpBreakdownWidget(widgetData: LcpBreakdownAiWidget): Promise
|
|
|
802
847
|
};
|
|
803
848
|
}
|
|
804
849
|
|
|
850
|
+
async function makeBottomUpTimelineTreeWidget(widgetData: BottomUpTreeAiWidget): Promise<WidgetMakerResponse|null> {
|
|
851
|
+
const bottomUpRootNode = AiAssistanceModel.AIQueries.AIQueries.mainThreadActivityBottomUp(
|
|
852
|
+
widgetData.data.bounds, widgetData.data.parsedTrace);
|
|
853
|
+
if (!bottomUpRootNode) {
|
|
854
|
+
return null;
|
|
855
|
+
}
|
|
856
|
+
const events = bottomUpRootNode.events;
|
|
857
|
+
const startTime = Trace.Helpers.Timing.microToMilli(widgetData.data.bounds.min);
|
|
858
|
+
const endTime = Trace.Helpers.Timing.microToMilli(widgetData.data.bounds.max);
|
|
859
|
+
|
|
860
|
+
const renderedWidget = html`<devtools-widget
|
|
861
|
+
class="bottom-up-timeline-tree-widget"
|
|
862
|
+
${widget(Timeline.TimelineTreeView.BottomUpTimelineTreeView, {
|
|
863
|
+
selectedEvents: events,
|
|
864
|
+
parsedTrace: widgetData.data.parsedTrace,
|
|
865
|
+
startTime,
|
|
866
|
+
endTime,
|
|
867
|
+
compactMode: true,
|
|
868
|
+
maxLinkLength: 15,
|
|
869
|
+
})}></devtools-widget>`;
|
|
870
|
+
|
|
871
|
+
return {
|
|
872
|
+
renderedWidget,
|
|
873
|
+
revealable: new TimelineUtils.Helpers.RevealableBottomUpProfile(widgetData.data.bounds),
|
|
874
|
+
title: lockedString(UIStringsNotTranslate.bottomUpTree)
|
|
875
|
+
};
|
|
876
|
+
}
|
|
877
|
+
|
|
805
878
|
function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTemplate {
|
|
806
879
|
if (response === null) {
|
|
807
880
|
return Lit.nothing;
|
|
@@ -911,6 +984,7 @@ async function makeDomTreeWidget(widgetData: DomTreeAiWidget): Promise<WidgetMak
|
|
|
911
984
|
rootDOMNode: root,
|
|
912
985
|
visibleWidth: 400,
|
|
913
986
|
wrap: true,
|
|
987
|
+
maxRows: 10,
|
|
914
988
|
})}></devtools-widget>
|
|
915
989
|
`;
|
|
916
990
|
// clang-format on
|
|
@@ -951,7 +1025,7 @@ async function renderWidgets(
|
|
|
951
1025
|
response = await makeComputedStyleWidget(widgetData);
|
|
952
1026
|
break;
|
|
953
1027
|
case 'CORE_VITALS':
|
|
954
|
-
response = await
|
|
1028
|
+
response = await makeCoreWebVitalsWidget(widgetData);
|
|
955
1029
|
break;
|
|
956
1030
|
case 'STYLE_PROPERTIES':
|
|
957
1031
|
response = await makeStylePropertiesWidget(widgetData);
|
|
@@ -968,6 +1042,9 @@ async function renderWidgets(
|
|
|
968
1042
|
case 'TIMELINE_RANGE_SUMMARY':
|
|
969
1043
|
response = await makeTimelineRangeSummaryWidget(widgetData);
|
|
970
1044
|
break;
|
|
1045
|
+
case 'BOTTOM_UP_TREE':
|
|
1046
|
+
response = await makeBottomUpTimelineTreeWidget(widgetData);
|
|
1047
|
+
break;
|
|
971
1048
|
default:
|
|
972
1049
|
Platform.assertNever(widgetData, 'Unknown AiWidget name');
|
|
973
1050
|
}
|
|
@@ -1066,9 +1143,14 @@ function renderImageChatMessage(inlineData: Host.AidaClient.MediaBlob): Lit.LitT
|
|
|
1066
1143
|
}
|
|
1067
1144
|
|
|
1068
1145
|
function renderActions(input: ChatMessageViewInput, output: ViewOutput): Lit.LitTemplate {
|
|
1146
|
+
const aiAssistanceV2 = Root.Runtime.hostConfig.devToolsAiAssistanceV2?.enabled;
|
|
1147
|
+
const rowClasses = Lit.Directives.classMap({
|
|
1148
|
+
'ai-assistance-feedback-row': true,
|
|
1149
|
+
'not-v2': !aiAssistanceV2,
|
|
1150
|
+
});
|
|
1069
1151
|
// clang-format off
|
|
1070
1152
|
return html`
|
|
1071
|
-
<div class
|
|
1153
|
+
<div class=${rowClasses}>
|
|
1072
1154
|
<div class="action-buttons">
|
|
1073
1155
|
${input.showRateButtons ? html`
|
|
1074
1156
|
<devtools-button
|
|
@@ -1097,7 +1179,7 @@ function renderActions(input: ChatMessageViewInput, output: ViewOutput): Lit.Lit
|
|
|
1097
1179
|
} as Buttons.Button.ButtonData}
|
|
1098
1180
|
@click=${() => input.onRatingClick(Host.AidaClient.Rating.NEGATIVE)}
|
|
1099
1181
|
></devtools-button>
|
|
1100
|
-
|
|
1182
|
+
${aiAssistanceV2 ? Lit.nothing : html`<div class="vertical-separator"></div>`}
|
|
1101
1183
|
`: Lit.nothing}
|
|
1102
1184
|
<devtools-button
|
|
1103
1185
|
.data=${
|
|
@@ -1111,7 +1193,8 @@ function renderActions(input: ChatMessageViewInput, output: ViewOutput): Lit.Lit
|
|
|
1111
1193
|
}
|
|
1112
1194
|
@click=${input.onReportClick}
|
|
1113
1195
|
></devtools-button>
|
|
1114
|
-
|
|
1196
|
+
${aiAssistanceV2 ? Lit.nothing : html`
|
|
1197
|
+
<div class="vertical-separator"></div>
|
|
1115
1198
|
<devtools-button
|
|
1116
1199
|
.data=${{
|
|
1117
1200
|
variant: Buttons.Button.Variant.ICON,
|
|
@@ -1122,6 +1205,17 @@ function renderActions(input: ChatMessageViewInput, output: ViewOutput): Lit.Lit
|
|
|
1122
1205
|
} as Buttons.Button.ButtonData}
|
|
1123
1206
|
aria-label=${lockedString(UIStringsNotTranslate.copyResponse)}
|
|
1124
1207
|
@click=${input.onCopyResponseClick}></devtools-button>
|
|
1208
|
+
`}
|
|
1209
|
+
${input.onExportClick && aiAssistanceV2 && input.isLastMessage ? html`
|
|
1210
|
+
<devtools-button
|
|
1211
|
+
class="export-for-agents-button"
|
|
1212
|
+
.jslogContext=${'ai-export-for-agents'}
|
|
1213
|
+
.variant=${Buttons.Button.Variant.OUTLINED}
|
|
1214
|
+
.iconName=${'copy'}
|
|
1215
|
+
@click=${input.onExportClick}
|
|
1216
|
+
>${lockedString(UIStringsNotTranslate.exportForAgents)}</devtools-button>
|
|
1217
|
+
${input.suggestions ? html`<div class="vertical-separator"></div>` : Lit.nothing}
|
|
1218
|
+
` : Lit.nothing}
|
|
1125
1219
|
</div>
|
|
1126
1220
|
${input.suggestions ? html`<div class="suggestions-container">
|
|
1127
1221
|
<div class="scroll-button-container left hidden" ${ref(element => { output.suggestionsLeftScrollButtonContainer = element; } )}>
|
|
@@ -1225,11 +1319,14 @@ export class ChatMessage extends UI.Widget.Widget {
|
|
|
1225
1319
|
isReadOnly = false;
|
|
1226
1320
|
canShowFeedbackForm = false;
|
|
1227
1321
|
isLastMessage = false;
|
|
1322
|
+
isFirstMessage = false;
|
|
1228
1323
|
markdownRenderer!: MarkdownLitRenderer;
|
|
1229
1324
|
onSuggestionClick: (suggestion: string) => void = () => {};
|
|
1230
1325
|
onFeedbackSubmit:
|
|
1231
1326
|
(rpcId: Host.AidaClient.RpcGlobalId, rate: Host.AidaClient.Rating, feedback?: string) => void = () => {};
|
|
1232
1327
|
onCopyResponseClick: (message: ModelChatMessage) => void = () => {};
|
|
1328
|
+
onExportClick: () => void = () => {};
|
|
1329
|
+
changeSummary?: string;
|
|
1233
1330
|
walkthrough: MessageInput['walkthrough'] = {
|
|
1234
1331
|
onOpen: () => {},
|
|
1235
1332
|
onToggle: () => {},
|
|
@@ -1272,6 +1369,7 @@ export class ChatMessage extends UI.Widget.Widget {
|
|
|
1272
1369
|
canShowFeedbackForm: this.canShowFeedbackForm,
|
|
1273
1370
|
markdownRenderer: this.markdownRenderer,
|
|
1274
1371
|
isLastMessage: this.isLastMessage,
|
|
1372
|
+
isFirstMessage: this.isFirstMessage,
|
|
1275
1373
|
onSuggestionClick: this.onSuggestionClick,
|
|
1276
1374
|
onRatingClick: this.#handleRateClick.bind(this),
|
|
1277
1375
|
onReportClick: () => UIHelpers.openInNewTab(REPORT_URL),
|
|
@@ -1280,6 +1378,7 @@ export class ChatMessage extends UI.Widget.Widget {
|
|
|
1280
1378
|
this.onCopyResponseClick(this.message);
|
|
1281
1379
|
}
|
|
1282
1380
|
},
|
|
1381
|
+
onExportClick: this.onExportClick,
|
|
1283
1382
|
scrollSuggestionsScrollContainer: this.#scrollSuggestionsScrollContainer.bind(this),
|
|
1284
1383
|
onSuggestionsScrollOrResize: this.#handleSuggestionsScrollOrResize.bind(this),
|
|
1285
1384
|
onSubmit: this.#handleSubmit.bind(this),
|
|
@@ -1296,6 +1395,7 @@ export class ChatMessage extends UI.Widget.Widget {
|
|
|
1296
1395
|
currentRating: this.#currentRating,
|
|
1297
1396
|
isShowingFeedbackForm: this.#isShowingFeedbackForm,
|
|
1298
1397
|
onFeedbackSubmit: this.onFeedbackSubmit,
|
|
1398
|
+
changeSummary: this.changeSummary,
|
|
1299
1399
|
walkthrough: this.walkthrough,
|
|
1300
1400
|
},
|
|
1301
1401
|
this.#viewOutput, this.contentElement);
|
|
@@ -1436,7 +1536,7 @@ async function makeTimelineRangeSummaryWidget(widgetData: TimelineRangeSummaryAi
|
|
|
1436
1536
|
widget(Timeline.ThirdPartyTreeView.ThirdPartyTreeViewWidget, {
|
|
1437
1537
|
maxRows: 10,
|
|
1438
1538
|
model: {
|
|
1439
|
-
selectedEvents: thirdPartyTree.selectedEvents
|
|
1539
|
+
selectedEvents: thirdPartyTree.selectedEvents ?? null,
|
|
1440
1540
|
parsedTrace,
|
|
1441
1541
|
entityMapper: thirdPartyTree.entityMapper(),
|
|
1442
1542
|
},
|
|
@@ -104,9 +104,12 @@ interface ChatWidgetInput extends Props {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
const DEFAULT_VIEW: View = (input, output, target) => {
|
|
107
|
+
const hasAiV2 = Boolean(Root.Runtime.hostConfig.devToolsAiAssistanceV2?.enabled);
|
|
108
|
+
|
|
107
109
|
const chatUiClasses = classMap({
|
|
108
110
|
'chat-ui': true,
|
|
109
111
|
gemini: AiAssistanceModel.AiUtils.isGeminiBranding(),
|
|
112
|
+
'ai-v2': hasAiV2,
|
|
110
113
|
});
|
|
111
114
|
|
|
112
115
|
const inputWidgetClasses = classMap({
|
|
@@ -114,8 +117,7 @@ const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
114
117
|
sticky: !input.isReadOnly,
|
|
115
118
|
});
|
|
116
119
|
|
|
117
|
-
const
|
|
118
|
-
const shouldShowExportToAgent = !input.isLoading && input.messages.length > 0 && hasAiV2Flag;
|
|
120
|
+
const shouldShowPatchWidget = !hasAiV2 && !input.isLoading;
|
|
119
121
|
|
|
120
122
|
// clang-format off
|
|
121
123
|
render(html`
|
|
@@ -132,27 +134,21 @@ const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
132
134
|
canShowFeedbackForm: input.canShowFeedbackForm,
|
|
133
135
|
markdownRenderer: input.markdownRenderer,
|
|
134
136
|
isLastMessage: input.messages.at(-1) === message,
|
|
137
|
+
isFirstMessage: input.messages.at(0) === message,
|
|
135
138
|
onSuggestionClick: input.handleSuggestionClick,
|
|
136
139
|
onFeedbackSubmit: input.onFeedbackSubmit,
|
|
137
140
|
onCopyResponseClick: input.onCopyResponseClick,
|
|
141
|
+
onExportClick: input.exportForAgentsClick,
|
|
142
|
+
changeSummary: input.changeSummary,
|
|
138
143
|
walkthrough: {
|
|
139
144
|
...input.walkthrough,
|
|
140
145
|
}
|
|
141
146
|
})
|
|
142
147
|
)}
|
|
143
|
-
${
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
.variant=${Buttons.Button.Variant.TEXT}
|
|
148
|
-
.iconName=${'copy'}
|
|
149
|
-
@click=${input.exportForAgentsClick}
|
|
150
|
-
>Export for agents</devtools-button>
|
|
151
|
-
` : nothing}
|
|
152
|
-
${input.isLoading ? nothing : widget(PatchWidget, {
|
|
153
|
-
changeSummary: input.changeSummary ?? '',
|
|
154
|
-
changeManager: input.changeManager,
|
|
155
|
-
})}
|
|
148
|
+
${shouldShowPatchWidget ? widget(PatchWidget, {
|
|
149
|
+
changeSummary: input.changeSummary ?? '',
|
|
150
|
+
changeManager: input.changeManager,
|
|
151
|
+
}) : nothing}
|
|
156
152
|
</div>
|
|
157
153
|
` : html`
|
|
158
154
|
<div class="empty-state-container">
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
|
+
import '../../../ui/components/spinners/spinners.js';
|
|
6
|
+
|
|
5
7
|
import * as Host from '../../../core/host/host.js';
|
|
6
8
|
import * as i18n from '../../../core/i18n/i18n.js';
|
|
7
9
|
import * as Buttons from '../../../ui/components/buttons/buttons.js';
|
|
@@ -17,7 +19,7 @@ const UIStrings = {
|
|
|
17
19
|
/**
|
|
18
20
|
* @description Title for the export for agents dialog.
|
|
19
21
|
*/
|
|
20
|
-
exportForAgents: '
|
|
22
|
+
exportForAgents: 'Copy for your coding agent',
|
|
21
23
|
/**
|
|
22
24
|
* @description Button text for copying to clipboard.
|
|
23
25
|
*/
|
|
@@ -80,6 +82,7 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
|
|
|
80
82
|
i18nString(UIStrings.generatingSummary) :
|
|
81
83
|
(isPrompt ? input.state.promptText : input.state.conversationText);
|
|
82
84
|
// clang-format off
|
|
85
|
+
|
|
83
86
|
render(html`
|
|
84
87
|
<style>${styles}</style>
|
|
85
88
|
<div class="export-for-agents-dialog">
|
|
@@ -111,9 +114,15 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
|
|
|
111
114
|
</label>
|
|
112
115
|
</div>
|
|
113
116
|
<main>
|
|
114
|
-
|
|
117
|
+
${input.state.isPromptLoading ? html`
|
|
118
|
+
<span class="prompt-loading">
|
|
119
|
+
<devtools-spinner></devtools-spinner>
|
|
120
|
+
${i18nString(UIStrings.generatingSummary)}
|
|
121
|
+
</span>
|
|
122
|
+
` : Lit.nothing}
|
|
123
|
+
<textarea readonly .value=${input.state.isPromptLoading ? '' : exportText}></textarea>
|
|
115
124
|
</main>
|
|
116
|
-
|
|
125
|
+
<div class="disclaimer">${i18nString(UIStrings.disclaimer)}</div>
|
|
117
126
|
<footer>
|
|
118
127
|
<div class="right-buttons">
|
|
119
128
|
<devtools-button
|
|
@@ -156,7 +165,7 @@ export class ExportForAgentsDialog extends UI.Widget.VBox {
|
|
|
156
165
|
this.#onConversationSaveAs = options.onConversationSaveAs;
|
|
157
166
|
this.#view = view;
|
|
158
167
|
|
|
159
|
-
if (options.promptText
|
|
168
|
+
if (typeof options.promptText !== 'string') {
|
|
160
169
|
void options.promptText.then(promptText => {
|
|
161
170
|
this.#state.promptText = promptText;
|
|
162
171
|
this.#state.isPromptLoading = false;
|
|
@@ -9,10 +9,15 @@
|
|
|
9
9
|
font-family: var(--default-font-family);
|
|
10
10
|
width: 100%;
|
|
11
11
|
display: flex;
|
|
12
|
-
gap: var(--sys-size-8);
|
|
13
12
|
justify-content: space-between;
|
|
14
13
|
align-items: center;
|
|
15
14
|
margin-block: calc(-1 * var(--sys-size-3));
|
|
15
|
+
margin-top: var(--sys-size-5);
|
|
16
|
+
|
|
17
|
+
&.not-v2 {
|
|
18
|
+
/* Can be removed when AIv2 ships */
|
|
19
|
+
gap: var(--sys-size-8);
|
|
20
|
+
}
|
|
16
21
|
|
|
17
22
|
.action-buttons {
|
|
18
23
|
display: flex;
|
|
@@ -119,6 +124,13 @@
|
|
|
119
124
|
}
|
|
120
125
|
}
|
|
121
126
|
|
|
127
|
+
.user-query-wrapper {
|
|
128
|
+
display: flex;
|
|
129
|
+
justify-content: flex-end;
|
|
130
|
+
padding: 0 var(--sys-size-5);
|
|
131
|
+
align-items: center;
|
|
132
|
+
}
|
|
133
|
+
|
|
122
134
|
.chat-message {
|
|
123
135
|
user-select: text;
|
|
124
136
|
cursor: initial;
|
|
@@ -132,6 +144,42 @@
|
|
|
132
144
|
overflow-wrap: anywhere;
|
|
133
145
|
border-bottom: var(--sys-size-1) solid var(--sys-color-divider);
|
|
134
146
|
|
|
147
|
+
|
|
148
|
+
&.query.ai-v2 {
|
|
149
|
+
width: fit-content;
|
|
150
|
+
max-width: 80%;
|
|
151
|
+
text-align: left;
|
|
152
|
+
padding: var(--sys-size-4) var(--sys-size-6);
|
|
153
|
+
font: var(--sys-typescale-body4-regular);
|
|
154
|
+
/* top left - top right - bottom right - bottom left */
|
|
155
|
+
border-radius: var(--sys-shape-corner-medium) var(--sys-shape-corner-extra-small) var(--sys-shape-corner-medium) var(--sys-shape-corner-medium);
|
|
156
|
+
background-color: var(--sys-color-surface5);
|
|
157
|
+
color: var(--sys-color-on-surface);
|
|
158
|
+
|
|
159
|
+
&.is-first-message {
|
|
160
|
+
/* So the first message doesn't bump right against the top
|
|
161
|
+
* toolbar */
|
|
162
|
+
margin-top: var(--sys-size-6);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
&.ai-v2 {
|
|
167
|
+
border-bottom: none;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.ai-css-change {
|
|
171
|
+
margin: var(--sys-size-6) 0;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
&.ai-v2 .answer-body-wrapper {
|
|
175
|
+
@container(min-width: 700px) {
|
|
176
|
+
/* Purposefully not using design system variables, this is a
|
|
177
|
+
* specific size to indent the content in and align it with the
|
|
178
|
+
* walkthrough CTA. */
|
|
179
|
+
padding-left: 35px;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
135
183
|
&.is-last-message {
|
|
136
184
|
border-bottom: 0;
|
|
137
185
|
}
|
|
@@ -395,6 +443,10 @@
|
|
|
395
443
|
|
|
396
444
|
.widget-name {
|
|
397
445
|
font: var(--sys-typescale-body4-regular);
|
|
446
|
+
max-width: 80%;
|
|
447
|
+
overflow: hidden;
|
|
448
|
+
text-overflow: ellipsis;
|
|
449
|
+
white-space: nowrap; /* stop the titles going onto multiple lines */
|
|
398
450
|
}
|
|
399
451
|
|
|
400
452
|
.widget-reveal-container {
|