chrome-devtools-frontend 1.0.1516909 → 1.0.1518653
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/docs/checklist/README.md +2 -2
- package/docs/checklist/javascript.md +1 -1
- package/docs/contributing/README.md +1 -1
- package/docs/contributing/settings-experiments-features.md +9 -8
- package/docs/cookbook/devtools_on_devtools.md +2 -2
- package/docs/cookbook/localization.md +10 -10
- package/docs/devtools-protocol.md +9 -8
- package/docs/ecosystem/automatic_workspace_folders.md +3 -3
- package/docs/get_the_code.md +0 -2
- package/docs/styleguide/ux/components.md +166 -85
- package/docs/styleguide/ux/numbers.md +3 -4
- package/front_end/core/common/README.md +13 -12
- package/front_end/core/host/GdpClient.ts +16 -1
- package/front_end/core/host/UserMetrics.ts +4 -2
- package/front_end/core/root/Runtime.ts +13 -0
- package/front_end/core/sdk/CSSMatchedStyles.ts +5 -1
- package/front_end/entrypoints/main/MainImpl.ts +6 -3
- package/front_end/generated/InspectorBackendCommands.js +10 -7
- package/front_end/generated/SupportedCSSProperties.js +21 -7
- package/front_end/generated/protocol-mapping.d.ts +16 -1
- package/front_end/generated/protocol-proxy-api.d.ts +13 -1
- package/front_end/generated/protocol.ts +95 -0
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +166 -49
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +14 -181
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +13 -315
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +224 -50
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +310 -11
- package/front_end/models/ai_assistance/performance/AIContext.ts +15 -2
- package/front_end/models/ai_code_completion/AiCodeCompletion.ts +17 -11
- package/front_end/models/badges/Badge.ts +8 -3
- package/front_end/models/badges/CodeWhispererBadge.ts +2 -4
- package/front_end/models/badges/StarterBadge.ts +2 -2
- package/front_end/models/badges/UserBadges.ts +21 -3
- package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
- package/front_end/models/trace/README.md +28 -1
- package/front_end/models/trace/handlers/UserTimingsHandler.ts +1 -1
- package/front_end/models/trace/helpers/Trace.ts +99 -43
- package/front_end/models/trace/types/TraceEvents.ts +9 -0
- package/front_end/panels/accessibility/ARIAAttributesView.ts +113 -191
- package/front_end/panels/accessibility/AccessibilityNodeView.ts +9 -9
- package/front_end/panels/accessibility/AccessibilitySubPane.ts +6 -4
- package/front_end/panels/accessibility/accessibilityProperties.css +2 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +16 -2
- package/front_end/panels/ai_assistance/components/ChatView.ts +9 -10
- package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +42 -0
- package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +32 -9
- package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +7 -1
- package/front_end/panels/common/BadgeNotification.ts +21 -5
- package/front_end/panels/common/GdpSignUpDialog.ts +18 -9
- package/front_end/panels/console/ConsolePrompt.ts +1 -1
- package/front_end/panels/console/ConsoleView.ts +6 -2
- package/front_end/panels/elements/ElementsPanel.ts +4 -0
- package/front_end/panels/elements/ElementsTreeElement.ts +18 -0
- package/front_end/panels/elements/ElementsTreeOutline.ts +13 -0
- package/front_end/panels/elements/StylePropertyTreeElement.ts +21 -6
- package/front_end/panels/media/TickingFlameChart.ts +1 -1
- package/front_end/panels/profiler/HeapSnapshotView.ts +34 -19
- package/front_end/panels/search/SearchResultsPane.ts +124 -128
- package/front_end/panels/search/SearchView.ts +24 -17
- package/front_end/panels/settings/components/SyncSection.ts +16 -8
- package/front_end/panels/sources/AiCodeCompletionPlugin.ts +6 -1
- package/front_end/panels/sources/SourcesPanel.ts +3 -0
- package/front_end/panels/timeline/AppenderUtils.ts +2 -2
- package/front_end/panels/timeline/ExtensionTrackAppender.ts +13 -4
- package/front_end/panels/timeline/GPUTrackAppender.ts +2 -1
- package/front_end/panels/timeline/InteractionsTrackAppender.ts +5 -1
- package/front_end/panels/timeline/LayoutShiftsTrackAppender.ts +2 -1
- package/front_end/panels/timeline/ThreadAppender.ts +12 -3
- package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +9 -4
- package/front_end/panels/timeline/TimelinePanel.ts +3 -2
- package/front_end/panels/timeline/TimelineUIUtils.ts +5 -4
- package/front_end/panels/timeline/TimingsTrackAppender.ts +6 -1
- package/front_end/panels/timeline/components/CPUThrottlingSelector.ts +95 -82
- package/front_end/panels/timeline/components/LiveMetricsView.ts +2 -2
- package/front_end/panels/timeline/components/cpuThrottlingSelector.css +17 -15
- package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +3 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -1
- package/front_end/third_party/codemirror.next/codemirror.next.d.ts +6 -9
- package/front_end/third_party/codemirror.next/package.json +2 -1
- package/front_end/third_party/diff/README.chromium +1 -0
- package/front_end/ui/components/text_editor/config.ts +6 -7
- package/front_end/ui/components/tooltips/Tooltip.ts +70 -31
- package/front_end/ui/legacy/README.md +33 -24
- package/front_end/ui/legacy/SearchableView.ts +19 -26
- package/front_end/ui/legacy/TextPrompt.ts +166 -1
- package/front_end/ui/legacy/Treeoutline.ts +16 -2
- package/front_end/ui/legacy/UIUtils.ts +15 -2
- package/front_end/ui/legacy/XElement.ts +0 -43
- package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +20 -4
- package/front_end/ui/visual_logging/KnownContextValues.ts +19 -6
- package/front_end/ui/visual_logging/README.md +43 -27
- package/package.json +1 -1
@@ -35,6 +35,8 @@ import {
|
|
35
35
|
type Step
|
36
36
|
} from './components/ChatView.js';
|
37
37
|
import {ExploreWidget} from './components/ExploreWidget.js';
|
38
|
+
import {MarkdownRendererWithCodeBlock} from './components/MarkdownRendererWithCodeBlock.js';
|
39
|
+
import {PerformanceAgentMarkdownRenderer} from './components/PerformanceAgentMarkdownRenderer.js';
|
38
40
|
import {isAiAssistancePatchingEnabled} from './PatchWidget.js';
|
39
41
|
|
40
42
|
const {html} = Lit;
|
@@ -263,6 +265,16 @@ async function getEmptyStateSuggestions(
|
|
263
265
|
}
|
264
266
|
}
|
265
267
|
|
268
|
+
function getMarkdownRenderer(context: AiAssistanceModel.ConversationContext<unknown>|null):
|
269
|
+
MarkdownRendererWithCodeBlock {
|
270
|
+
if (context instanceof AiAssistanceModel.PerformanceTraceContext && !context.external) {
|
271
|
+
const focus = context.getItem();
|
272
|
+
return new PerformanceAgentMarkdownRenderer(focus.lookupEvent.bind(focus));
|
273
|
+
}
|
274
|
+
|
275
|
+
return new MarkdownRendererWithCodeBlock();
|
276
|
+
}
|
277
|
+
|
266
278
|
interface ToolbarViewInput {
|
267
279
|
onNewChatClick: () => void;
|
268
280
|
populateHistoryMenu: (contextMenu: UI.ContextMenu.ContextMenu) => void;
|
@@ -837,6 +849,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
837
849
|
|
838
850
|
override async performUpdate(): Promise<void> {
|
839
851
|
const emptyStateSuggestions = await getEmptyStateSuggestions(this.#selectedContext, this.#conversation);
|
852
|
+
const markdownRenderer = getMarkdownRenderer(this.#selectedContext);
|
840
853
|
|
841
854
|
this.view(
|
842
855
|
{
|
@@ -865,6 +878,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
865
878
|
changeManager: this.#changeManager,
|
866
879
|
uploadImageInputEnabled: isAiAssistanceMultimodalUploadInputEnabled() &&
|
867
880
|
this.#conversation?.type === AiAssistanceModel.ConversationType.STYLING,
|
881
|
+
markdownRenderer,
|
868
882
|
onNewChatClick: this.#handleNewChatRequest.bind(this),
|
869
883
|
populateHistoryMenu: this.#populateHistoryMenu.bind(this),
|
870
884
|
onDeleteClick: this.#onDeleteClicked.bind(this),
|
@@ -1054,8 +1068,8 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
1054
1068
|
const focus = context.getItem().data;
|
1055
1069
|
if (focus.callTree) {
|
1056
1070
|
const event = focus.callTree.selectedNode?.event ?? focus.callTree.rootNode.event;
|
1057
|
-
const
|
1058
|
-
return Common.Revealer.reveal(
|
1071
|
+
const revealable = new SDK.TraceObject.RevealableEvent(event);
|
1072
|
+
return Common.Revealer.reveal(revealable);
|
1059
1073
|
}
|
1060
1074
|
if (focus.insight) {
|
1061
1075
|
return Common.Revealer.reveal(focus.insight);
|
@@ -18,13 +18,13 @@ import * as PanelUtils from '../../../panels/utils/utils.js';
|
|
18
18
|
import * as Marked from '../../../third_party/marked/marked.js';
|
19
19
|
import * as Buttons from '../../../ui/components/buttons/buttons.js';
|
20
20
|
import type * as MarkdownView from '../../../ui/components/markdown_view/markdown_view.js';
|
21
|
+
import type {MarkdownLitRenderer} from '../../../ui/components/markdown_view/MarkdownView.js';
|
21
22
|
import * as UI from '../../../ui/legacy/legacy.js';
|
22
23
|
import * as Lit from '../../../ui/lit/lit.js';
|
23
24
|
import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
|
24
25
|
import {PatchWidget} from '../PatchWidget.js';
|
25
26
|
|
26
27
|
import chatViewStyles from './chatView.css.js';
|
27
|
-
import {MarkdownRendererWithCodeBlock} from './MarkdownRendererWithCodeBlock.js';
|
28
28
|
import {UserActionRow} from './UserActionRow.js';
|
29
29
|
|
30
30
|
const {html, Directives: {ifDefined, ref}} = Lit;
|
@@ -303,11 +303,11 @@ export interface Props {
|
|
303
303
|
disclaimerText: Platform.UIString.LocalizedString;
|
304
304
|
isTextInputEmpty: boolean;
|
305
305
|
uploadImageInputEnabled?: boolean;
|
306
|
+
markdownRenderer: MarkdownLitRenderer;
|
306
307
|
}
|
307
308
|
|
308
309
|
export class ChatView extends HTMLElement {
|
309
310
|
readonly #shadow = this.attachShadow({mode: 'open'});
|
310
|
-
#markdownRenderer = new MarkdownRendererWithCodeBlock();
|
311
311
|
#scrollTop?: number;
|
312
312
|
#props: Props;
|
313
313
|
#messagesContainerElement?: Element;
|
@@ -338,7 +338,6 @@ export class ChatView extends HTMLElement {
|
|
338
338
|
}
|
339
339
|
|
340
340
|
set props(props: Props) {
|
341
|
-
this.#markdownRenderer = new MarkdownRendererWithCodeBlock();
|
342
341
|
this.#props = props;
|
343
342
|
this.#render();
|
344
343
|
}
|
@@ -624,7 +623,7 @@ export class ChatView extends HTMLElement {
|
|
624
623
|
isTextInputDisabled: this.#props.isTextInputDisabled,
|
625
624
|
suggestions: this.#props.emptyStateSuggestions,
|
626
625
|
userInfo: this.#props.userInfo,
|
627
|
-
markdownRenderer: this.#markdownRenderer,
|
626
|
+
markdownRenderer: this.#props.markdownRenderer,
|
628
627
|
conversationType: this.#props.conversationType,
|
629
628
|
changeSummary: this.#props.changeSummary,
|
630
629
|
changeManager: this.#props.changeManager,
|
@@ -672,7 +671,7 @@ export class ChatView extends HTMLElement {
|
|
672
671
|
}
|
673
672
|
}
|
674
673
|
|
675
|
-
function renderTextAsMarkdown(text: string, markdownRenderer:
|
674
|
+
function renderTextAsMarkdown(text: string, markdownRenderer: MarkdownLitRenderer, {animate, ref: refFn}: {
|
676
675
|
animate?: boolean,
|
677
676
|
ref?: (element?: Element) => void,
|
678
677
|
} = {}): Lit.TemplateResult {
|
@@ -753,7 +752,7 @@ function renderStepDetails({
|
|
753
752
|
isLast,
|
754
753
|
}: {
|
755
754
|
step: Step,
|
756
|
-
markdownRenderer:
|
755
|
+
markdownRenderer: MarkdownLitRenderer,
|
757
756
|
isLast: boolean,
|
758
757
|
}): Lit.LitTemplate {
|
759
758
|
const sideEffects = isLast && step.sideEffect ? renderSideEffectConfirmationUi(step) : Lit.nothing;
|
@@ -817,7 +816,7 @@ function renderStepBadge({step, isLoading, isLast}: {
|
|
817
816
|
function renderStep({step, isLoading, markdownRenderer, isLast}: {
|
818
817
|
step: Step,
|
819
818
|
isLoading: boolean,
|
820
|
-
markdownRenderer:
|
819
|
+
markdownRenderer: MarkdownLitRenderer,
|
821
820
|
isLast: boolean,
|
822
821
|
}): Lit.LitTemplate {
|
823
822
|
const stepClasses = Lit.Directives.classMap({
|
@@ -926,7 +925,7 @@ function renderChatMessage({
|
|
926
925
|
canShowFeedbackForm: boolean,
|
927
926
|
isLast: boolean,
|
928
927
|
userInfo: Pick<Host.InspectorFrontendHostAPI.SyncInformation, 'accountImage'|'accountFullName'>,
|
929
|
-
markdownRenderer:
|
928
|
+
markdownRenderer: MarkdownLitRenderer,
|
930
929
|
onSuggestionClick: (suggestion: string) => void,
|
931
930
|
onFeedbackSubmit: (rpcId: Host.AidaClient.RpcGlobalId, rate: Host.AidaClient.Rating, feedback?: string) => void,
|
932
931
|
onCopyResponseClick: (message: ModelChatMessage) => void,
|
@@ -1156,7 +1155,7 @@ function renderMessages({
|
|
1156
1155
|
isReadOnly: boolean,
|
1157
1156
|
canShowFeedbackForm: boolean,
|
1158
1157
|
userInfo: Pick<Host.InspectorFrontendHostAPI.SyncInformation, 'accountImage'|'accountFullName'>,
|
1159
|
-
markdownRenderer:
|
1158
|
+
markdownRenderer: MarkdownLitRenderer,
|
1160
1159
|
onSuggestionClick: (suggestion: string) => void,
|
1161
1160
|
onFeedbackSubmit: (rpcId: Host.AidaClient.RpcGlobalId, rate: Host.AidaClient.Rating, feedback?: string) => void,
|
1162
1161
|
onCopyResponseClick: (message: ModelChatMessage) => void,
|
@@ -1657,7 +1656,7 @@ function renderMainContents({
|
|
1657
1656
|
isTextInputDisabled: boolean,
|
1658
1657
|
suggestions: AiAssistanceModel.ConversationSuggestion[],
|
1659
1658
|
userInfo: Pick<Host.InspectorFrontendHostAPI.SyncInformation, 'accountImage'|'accountFullName'>,
|
1660
|
-
markdownRenderer:
|
1659
|
+
markdownRenderer: MarkdownLitRenderer,
|
1661
1660
|
changeManager: AiAssistanceModel.ChangeManager,
|
1662
1661
|
onSuggestionClick: (suggestion: string) => void,
|
1663
1662
|
onFeedbackSubmit: (rpcId: Host.AidaClient.RpcGlobalId, rate: Host.AidaClient.Rating, feedback?: string) => void,
|
@@ -0,0 +1,42 @@
|
|
1
|
+
// Copyright 2025 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 Common from '../../../core/common/common.js';
|
6
|
+
import * as SDK from '../../../core/sdk/sdk.js';
|
7
|
+
import * as Trace from '../../../models/trace/trace.js';
|
8
|
+
import type * as Marked from '../../../third_party/marked/marked.js';
|
9
|
+
import * as Lit from '../../../ui/lit/lit.js';
|
10
|
+
|
11
|
+
import {MarkdownRendererWithCodeBlock} from './MarkdownRendererWithCodeBlock.js';
|
12
|
+
|
13
|
+
const {html} = Lit;
|
14
|
+
|
15
|
+
export class PerformanceAgentMarkdownRenderer extends MarkdownRendererWithCodeBlock {
|
16
|
+
constructor(private lookupEvent: (key: Trace.Types.File.SerializableKey) => Trace.Types.Events.Event | null) {
|
17
|
+
super();
|
18
|
+
}
|
19
|
+
|
20
|
+
override templateForToken(token: Marked.Marked.MarkedToken): Lit.TemplateResult|null {
|
21
|
+
if (token.type === 'link' && token.href.startsWith('#')) {
|
22
|
+
const event = this.lookupEvent(token.href.slice(1) as Trace.Types.File.SerializableKey);
|
23
|
+
if (event) {
|
24
|
+
let label = token.text;
|
25
|
+
let title = '';
|
26
|
+
if (Trace.Types.Events.isSyntheticNetworkRequest(event)) {
|
27
|
+
title = event.args.data.url;
|
28
|
+
} else {
|
29
|
+
label += ` (${event.name})`;
|
30
|
+
}
|
31
|
+
|
32
|
+
// eslint-disable-next-line rulesdir/no-a-tags-in-lit
|
33
|
+
return html`<a href="#" draggable=false .title=${title} @click=${(e: Event) => {
|
34
|
+
e.stopPropagation();
|
35
|
+
void Common.Revealer.reveal(new SDK.TraceObject.RevealableEvent(event));
|
36
|
+
}}>${label}</a>`;
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
return super.templateForToken(token);
|
41
|
+
}
|
42
|
+
}
|
@@ -33,6 +33,10 @@ const UIStringsNotTranslate = {
|
|
33
33
|
*/
|
34
34
|
tooltipDisclaimerTextForAiCodeCompletionNoLogging:
|
35
35
|
'To generate code suggestions, your console input and the history of your current console session are shared with Google. This data will not be used to improve Google’s AI models.',
|
36
|
+
/**
|
37
|
+
* Text for tooltip shown on hovering over spinner.
|
38
|
+
*/
|
39
|
+
tooltipTextForSpinner: 'Shows when data is being sent to Google to generate code suggestions',
|
36
40
|
/**
|
37
41
|
* @description Text for tooltip button which redirects to AI settings
|
38
42
|
*/
|
@@ -47,6 +51,7 @@ const lockedString = i18n.i18n.lockedString;
|
|
47
51
|
|
48
52
|
export interface ViewInput {
|
49
53
|
disclaimerTooltipId?: string;
|
54
|
+
spinnerTooltipId?: string;
|
50
55
|
noLogging: boolean;
|
51
56
|
aidaAvailability?: Host.AidaClient.AidaAccessPreconditions;
|
52
57
|
onManageInSettingsTooltipClick: () => void;
|
@@ -59,12 +64,14 @@ export interface ViewOutput {
|
|
59
64
|
|
60
65
|
export type View = (input: ViewInput, output: ViewOutput, target: HTMLElement) => void;
|
61
66
|
|
62
|
-
export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View =
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
67
|
+
export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View =
|
68
|
+
(input, output, target) => {
|
69
|
+
if (input.aidaAvailability !== Host.AidaClient.AidaAccessPreconditions.AVAILABLE || !input.disclaimerTooltipId ||
|
70
|
+
!input.spinnerTooltipId) {
|
71
|
+
render(nothing, target);
|
72
|
+
return;
|
73
|
+
}
|
74
|
+
// clang-format off
|
68
75
|
render(
|
69
76
|
html`
|
70
77
|
<style>${styles}</style>
|
@@ -76,7 +83,16 @@ export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View = (input, output, target) => {
|
|
76
83
|
el.toggleAttribute('active', isLoading);
|
77
84
|
};
|
78
85
|
}
|
79
|
-
})}
|
86
|
+
})}
|
87
|
+
aria-details=${input.spinnerTooltipId}
|
88
|
+
aria-describedby=${input.spinnerTooltipId}></devtools-spinner>
|
89
|
+
<devtools-tooltip
|
90
|
+
id=${input.spinnerTooltipId}
|
91
|
+
variant=${'rich'}
|
92
|
+
jslogContext=${'ai-code-completion-spinner-tooltip'}>
|
93
|
+
<div class="disclaimer-tooltip-container"><div class="tooltip-text">
|
94
|
+
${lockedString(UIStringsNotTranslate.tooltipTextForSpinner)}
|
95
|
+
</div></div></devtools-tooltip>
|
80
96
|
<span
|
81
97
|
tabIndex="0"
|
82
98
|
class="link"
|
@@ -115,8 +131,8 @@ export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View = (input, output, target) => {
|
|
115
131
|
>${lockedString(UIStringsNotTranslate.manageInSettings)}</span></div></devtools-tooltip>
|
116
132
|
</div>
|
117
133
|
`, target);
|
118
|
-
|
119
|
-
};
|
134
|
+
// clang-format on
|
135
|
+
};
|
120
136
|
|
121
137
|
const MINIMUM_LOADING_STATE_TIMEOUT = 1000;
|
122
138
|
|
@@ -124,6 +140,7 @@ export class AiCodeCompletionDisclaimer extends UI.Widget.Widget {
|
|
124
140
|
readonly #view: View;
|
125
141
|
#viewOutput: ViewOutput = {};
|
126
142
|
|
143
|
+
#spinnerTooltipId?: string;
|
127
144
|
#disclaimerTooltipId?: string;
|
128
145
|
#noLogging: boolean; // Whether the enterprise setting is `ALLOW_WITHOUT_LOGGING` or not.
|
129
146
|
#loading = false;
|
@@ -147,6 +164,11 @@ export class AiCodeCompletionDisclaimer extends UI.Widget.Widget {
|
|
147
164
|
this.requestUpdate();
|
148
165
|
}
|
149
166
|
|
167
|
+
set spinnerTooltipId(spinnerTooltipId: string) {
|
168
|
+
this.#spinnerTooltipId = spinnerTooltipId;
|
169
|
+
this.requestUpdate();
|
170
|
+
}
|
171
|
+
|
150
172
|
set loading(loading: boolean) {
|
151
173
|
if (!loading && !this.#loading) {
|
152
174
|
return;
|
@@ -191,6 +213,7 @@ export class AiCodeCompletionDisclaimer extends UI.Widget.Widget {
|
|
191
213
|
this.#view(
|
192
214
|
{
|
193
215
|
disclaimerTooltipId: this.#disclaimerTooltipId,
|
216
|
+
spinnerTooltipId: this.#spinnerTooltipId,
|
194
217
|
noLogging: this.#noLogging,
|
195
218
|
aidaAvailability: this.#aidaAvailability,
|
196
219
|
onManageInSettingsTooltipClick: this.#onManageInSettingsTooltipClick.bind(this),
|
@@ -30,11 +30,13 @@ const lockedString = i18n.i18n.lockedString;
|
|
30
30
|
export interface AiCodeCompletionSummaryToolbarProps {
|
31
31
|
citationsTooltipId: string;
|
32
32
|
disclaimerTooltipId?: string;
|
33
|
+
spinnerTooltipId?: string;
|
33
34
|
hasTopBorder?: boolean;
|
34
35
|
}
|
35
36
|
|
36
37
|
export interface ViewInput {
|
37
38
|
disclaimerTooltipId?: string;
|
39
|
+
spinnerTooltipId?: string;
|
38
40
|
citations?: Set<string>;
|
39
41
|
citationsTooltipId: string;
|
40
42
|
loading: boolean;
|
@@ -57,10 +59,11 @@ export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View = (input, _output, target) => {
|
|
57
59
|
});
|
58
60
|
|
59
61
|
// clang-format off
|
60
|
-
const disclaimer = input.disclaimerTooltipId ?
|
62
|
+
const disclaimer = input.disclaimerTooltipId && input.spinnerTooltipId ?
|
61
63
|
html`<devtools-widget
|
62
64
|
.widgetConfig=${UI.Widget.widgetConfig(AiCodeCompletionDisclaimer, {
|
63
65
|
disclaimerTooltipId: input.disclaimerTooltipId,
|
66
|
+
spinnerTooltipId: input.spinnerTooltipId,
|
64
67
|
loading: input.loading,
|
65
68
|
})} class="disclaimer-widget"></devtools-widget>` : nothing;
|
66
69
|
|
@@ -102,6 +105,7 @@ export class AiCodeCompletionSummaryToolbar extends UI.Widget.Widget {
|
|
102
105
|
readonly #view: View;
|
103
106
|
|
104
107
|
#disclaimerTooltipId?: string;
|
108
|
+
#spinnerTooltipId?: string;
|
105
109
|
#citationsTooltipId: string;
|
106
110
|
#citations = new Set<string>();
|
107
111
|
#loading = false;
|
@@ -113,6 +117,7 @@ export class AiCodeCompletionSummaryToolbar extends UI.Widget.Widget {
|
|
113
117
|
constructor(props: AiCodeCompletionSummaryToolbarProps, view?: View) {
|
114
118
|
super();
|
115
119
|
this.#disclaimerTooltipId = props.disclaimerTooltipId;
|
120
|
+
this.#spinnerTooltipId = props.spinnerTooltipId;
|
116
121
|
this.#citationsTooltipId = props.citationsTooltipId;
|
117
122
|
this.#hasTopBorder = props.hasTopBorder ?? false;
|
118
123
|
this.#boundOnAidaAvailabilityChange = this.#onAidaAvailabilityChange.bind(this);
|
@@ -147,6 +152,7 @@ export class AiCodeCompletionSummaryToolbar extends UI.Widget.Widget {
|
|
147
152
|
this.#view(
|
148
153
|
{
|
149
154
|
disclaimerTooltipId: this.#disclaimerTooltipId,
|
155
|
+
spinnerTooltipId: this.#spinnerTooltipId,
|
150
156
|
citations: this.#citations,
|
151
157
|
citationsTooltipId: this.#citationsTooltipId,
|
152
158
|
loading: this.#loading,
|
@@ -73,13 +73,14 @@ const AUTO_CLOSE_TIME_IN_MS = 30000;
|
|
73
73
|
|
74
74
|
export interface BadgeNotificationAction {
|
75
75
|
label: string;
|
76
|
-
jslogContext
|
76
|
+
jslogContext: string;
|
77
77
|
title?: string;
|
78
78
|
onClick: () => void;
|
79
79
|
}
|
80
80
|
|
81
81
|
export interface BadgeNotificationProperties {
|
82
82
|
message: HTMLElement|string;
|
83
|
+
jslogContext: string;
|
83
84
|
imageUri: string;
|
84
85
|
actions: BadgeNotificationAction[];
|
85
86
|
isStarterBadge: boolean;
|
@@ -115,8 +116,8 @@ const DEFAULT_VIEW = (input: ViewInput, _output: undefined, target: HTMLElement)
|
|
115
116
|
|
116
117
|
render(html`
|
117
118
|
<style>${badgeNotificationStyles}</style>
|
118
|
-
<div class="container">
|
119
|
-
<div class="badge-container">
|
119
|
+
<div class="container" jslog=${VisualLogging.dialog('badge-notification')}>
|
120
|
+
<div class="badge-container" jslog=${VisualLogging.item(input.jslogContext)}>
|
120
121
|
<img class="badge-image" role="presentation" src=${input.imageUri}>
|
121
122
|
</div>
|
122
123
|
<div class="action-and-text-container">
|
@@ -138,6 +139,7 @@ function revealBadgeSettings(): void {
|
|
138
139
|
}
|
139
140
|
|
140
141
|
export class BadgeNotification extends UI.Widget.Widget {
|
142
|
+
jslogContext = '';
|
141
143
|
message: HTMLElement|string = '';
|
142
144
|
imageUri = '';
|
143
145
|
actions: BadgeNotificationAction[] = [];
|
@@ -175,6 +177,7 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
175
177
|
this.imageUri = properties.imageUri;
|
176
178
|
this.actions = properties.actions;
|
177
179
|
this.isStarterBadge = properties.isStarterBadge;
|
180
|
+
this.jslogContext = properties.jslogContext;
|
178
181
|
this.requestUpdate();
|
179
182
|
this.show(document.body);
|
180
183
|
|
@@ -193,7 +196,7 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
193
196
|
const receiveBadgesSettingEnabled = Badges.UserBadges.instance().isReceiveBadgesSettingEnabled();
|
194
197
|
const googleDeveloperProgramLink = UI.XLink.XLink.create(
|
195
198
|
'https://developers.google.com/program', lockedString('Google Developer Program'), 'badge-link', undefined,
|
196
|
-
'
|
199
|
+
'program-link');
|
197
200
|
|
198
201
|
// If the user already has a GDP profile and the receive badges setting enabled,
|
199
202
|
// starter badge behaves as if it's an activity based badge.
|
@@ -208,9 +211,11 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
208
211
|
this.#show({
|
209
212
|
message: i18nFormatString(
|
210
213
|
UIStrings.starterBadgeAwardMessageSettingDisabled, {PH1: badge.title, PH2: googleDeveloperProgramLink}),
|
214
|
+
jslogContext: badge.name,
|
211
215
|
actions: [
|
212
216
|
{
|
213
217
|
label: i18nString(UIStrings.remindMeLater),
|
218
|
+
jslogContext: 'remind-me-later',
|
214
219
|
onClick: () => {
|
215
220
|
this.detach();
|
216
221
|
Badges.UserBadges.instance().snoozeStarterBadge();
|
@@ -218,6 +223,7 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
218
223
|
},
|
219
224
|
{
|
220
225
|
label: i18nString(UIStrings.receiveBadges),
|
226
|
+
jslogContext: 'receive-badges',
|
221
227
|
onClick: () => {
|
222
228
|
this.detach();
|
223
229
|
revealBadgeSettings();
|
@@ -234,9 +240,11 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
234
240
|
this.#show({
|
235
241
|
message: i18nFormatString(
|
236
242
|
UIStrings.starterBadgeAwardMessageNoGdpProfile, {PH1: badge.title, PH2: googleDeveloperProgramLink}),
|
243
|
+
jslogContext: badge.name,
|
237
244
|
actions: [
|
238
245
|
{
|
239
246
|
label: i18nString(UIStrings.remindMeLater),
|
247
|
+
jslogContext: 'remind-me-later',
|
240
248
|
onClick: () => {
|
241
249
|
this.detach();
|
242
250
|
Badges.UserBadges.instance().snoozeStarterBadge();
|
@@ -244,9 +252,13 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
244
252
|
},
|
245
253
|
{
|
246
254
|
label: i18nString(UIStrings.createProfile),
|
255
|
+
jslogContext: 'create-profile',
|
247
256
|
onClick: () => {
|
248
257
|
this.detach();
|
249
|
-
GdpSignUpDialog.GdpSignUpDialog.show(
|
258
|
+
GdpSignUpDialog.GdpSignUpDialog.show({
|
259
|
+
// We want to consider cancelling from the starter badge as a "snooze" for starter badge.
|
260
|
+
onCancel: () => Badges.UserBadges.instance().snoozeStarterBadge(),
|
261
|
+
});
|
250
262
|
}
|
251
263
|
}
|
252
264
|
],
|
@@ -258,9 +270,11 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
258
270
|
#presentActivityBasedBadge(badge: Badges.Badge): void {
|
259
271
|
this.#show({
|
260
272
|
message: i18nString(UIStrings.activityBasedBadgeAwardMessage, {PH1: badge.title}),
|
273
|
+
jslogContext: badge.name,
|
261
274
|
actions: [
|
262
275
|
{
|
263
276
|
label: i18nString(UIStrings.manageSettings),
|
277
|
+
jslogContext: 'manage-settings',
|
264
278
|
onClick: () => {
|
265
279
|
this.detach();
|
266
280
|
revealBadgeSettings();
|
@@ -268,6 +282,7 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
268
282
|
},
|
269
283
|
{
|
270
284
|
label: i18nString(UIStrings.viewProfile),
|
285
|
+
jslogContext: 'view-profile',
|
271
286
|
onClick: () => {
|
272
287
|
UI.UIUtils.openInNewTab(Host.GdpClient.GOOGLE_DEVELOPER_PROGRAM_PROFILE_LINK);
|
273
288
|
}
|
@@ -310,6 +325,7 @@ export class BadgeNotification extends UI.Widget.Widget {
|
|
310
325
|
actions: this.actions,
|
311
326
|
isStarterBadge: this.isStarterBadge,
|
312
327
|
onDismissClick: this.#onDismissClick,
|
328
|
+
jslogContext: this.jslogContext,
|
313
329
|
};
|
314
330
|
this.#view(viewInput, undefined, this.contentElement);
|
315
331
|
}
|
@@ -137,7 +137,7 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
|
|
137
137
|
<devtools-switch
|
138
138
|
.checked=${input.keepMeUpdated}
|
139
139
|
@switchchange=${(e: Switch.Switch.SwitchChangeEvent) => input.onKeepMeUpdatedChange(e.checked)}
|
140
|
-
jslog=${VisualLogging.toggle('
|
140
|
+
jslog=${VisualLogging.toggle('keep-me-updated').track({ click: true })}
|
141
141
|
aria-label=${i18nString(UIStrings.keepUpdated)}
|
142
142
|
>
|
143
143
|
</devtools-switch>
|
@@ -152,11 +152,11 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
|
|
152
152
|
<div class="section-text">
|
153
153
|
<div>${i18nString(UIStrings.tailorProfileBody)}</div><br/>
|
154
154
|
<div>${i18n.i18n.getFormatLocalizedString(str_, UIStrings.tailorProfileBodyDisclaimer, {
|
155
|
-
PH1: UI.XLink.XLink.create(CONTENT_POLICY_URL, i18nString(UIStrings.contentPolicy), 'link', undefined, '
|
155
|
+
PH1: UI.XLink.XLink.create(CONTENT_POLICY_URL, i18nString(UIStrings.contentPolicy), 'link', undefined, 'content-policy'),
|
156
156
|
PH2: UI.XLink.XLink.create(TERMS_OF_SERVICE_URL, i18nString(UIStrings.termsOfService), 'link',
|
157
|
-
undefined, '
|
157
|
+
undefined, 'terms-of-service'),
|
158
158
|
PH3: UI.XLink.XLink.create(PRIVACY_POLICY_URL, i18nString(UIStrings.privacyPolicy), 'link',
|
159
|
-
undefined, '
|
159
|
+
undefined, 'privacy-policy'),
|
160
160
|
})}</div>
|
161
161
|
</div>
|
162
162
|
</div>
|
@@ -176,7 +176,7 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
|
|
176
176
|
@click=${input.onCancelClick}>${i18nString(UIStrings.cancel)}</devtools-button>
|
177
177
|
<devtools-button
|
178
178
|
.variant=${Buttons.Button.Variant.PRIMARY}
|
179
|
-
.jslogContext=${'
|
179
|
+
.jslogContext=${'sign-up'}
|
180
180
|
.spinner=${input.isSigningUp}
|
181
181
|
.disabled=${input.isSigningUp}
|
182
182
|
@click=${input.onSignUpClick}>${i18nString(UIStrings.signUp)}</devtools-button>
|
@@ -194,11 +194,19 @@ export class GdpSignUpDialog extends UI.Widget.VBox {
|
|
194
194
|
#keepMeUpdated = false;
|
195
195
|
#isSigningUp = false;
|
196
196
|
#onSuccess?: () => void;
|
197
|
+
#onCancel?: () => void;
|
197
198
|
|
198
|
-
constructor(
|
199
|
+
constructor(
|
200
|
+
options: {
|
201
|
+
dialog: UI.Dialog.Dialog,
|
202
|
+
onSuccess?: () => void,
|
203
|
+
onCancel?: () => void,
|
204
|
+
},
|
205
|
+
view?: View) {
|
199
206
|
super();
|
200
207
|
this.#dialog = options.dialog;
|
201
208
|
this.#onSuccess = options.onSuccess;
|
209
|
+
this.#onCancel = options.onCancel;
|
202
210
|
this.#view = view ?? DEFAULT_VIEW;
|
203
211
|
this.requestUpdate();
|
204
212
|
}
|
@@ -231,6 +239,7 @@ export class GdpSignUpDialog extends UI.Widget.VBox {
|
|
231
239
|
onSignUpClick: this.#onSignUpClick.bind(this),
|
232
240
|
onCancelClick: () => {
|
233
241
|
this.#dialog.hide();
|
242
|
+
this.#onCancel?.();
|
234
243
|
},
|
235
244
|
keepMeUpdated: this.#keepMeUpdated,
|
236
245
|
onKeepMeUpdatedChange: (value: boolean) => {
|
@@ -243,14 +252,14 @@ export class GdpSignUpDialog extends UI.Widget.VBox {
|
|
243
252
|
this.#view(viewInput, undefined, this.contentElement);
|
244
253
|
}
|
245
254
|
|
246
|
-
static show({onSuccess}: {onSuccess?: () => void} = {}): void {
|
247
|
-
const dialog = new UI.Dialog.Dialog();
|
255
|
+
static show({onSuccess, onCancel}: {onSuccess?: () => void, onCancel?: () => void} = {}): void {
|
256
|
+
const dialog = new UI.Dialog.Dialog('gdp-sign-up-dialog');
|
248
257
|
dialog.setAriaLabel(i18nString(UIStrings.gdpDialogAriaLabel));
|
249
258
|
dialog.setMaxContentSize(new Geometry.Size(384, 500));
|
250
259
|
dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.SET_EXACT_WIDTH_MAX_HEIGHT);
|
251
260
|
dialog.setDimmed(true);
|
252
261
|
|
253
|
-
new GdpSignUpDialog({dialog, onSuccess}).show(dialog.contentElement);
|
262
|
+
new GdpSignUpDialog({dialog, onSuccess, onCancel}).show(dialog.contentElement);
|
254
263
|
dialog.show(undefined, /* stack */ true);
|
255
264
|
}
|
256
265
|
}
|
@@ -529,7 +529,7 @@ export class ConsolePrompt extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
529
529
|
this.teaser = undefined;
|
530
530
|
}
|
531
531
|
this.aiCodeCompletion = new AiCodeCompletion.AiCodeCompletion.AiCodeCompletion(
|
532
|
-
{aidaClient: this.aidaClient}, this.editor, AiCodeCompletion.AiCodeCompletion.
|
532
|
+
{aidaClient: this.aidaClient}, this.editor, AiCodeCompletion.AiCodeCompletion.ContextFlavor.CONSOLE, ['\n\n']);
|
533
533
|
this.aiCodeCompletion.addEventListener(AiCodeCompletion.AiCodeCompletion.Events.RESPONSE_RECEIVED, event => {
|
534
534
|
this.aiCodeCompletionCitations = event.data.citations;
|
535
535
|
this.dispatchEventToListeners(Events.AI_CODE_COMPLETION_RESPONSE_RECEIVED, event.data);
|
@@ -270,6 +270,7 @@ let consoleViewInstance: ConsoleView;
|
|
270
270
|
|
271
271
|
const MIN_HISTORY_LENGTH_FOR_DISABLING_SELF_XSS_WARNING = 5;
|
272
272
|
const DISCLAIMER_TOOLTIP_ID = 'console-ai-code-completion-disclaimer-tooltip';
|
273
|
+
const SPINNER_TOOLTIP_ID = 'console-ai-code-completion-spinner-tooltip';
|
273
274
|
const CITATIONS_TOOLTIP_ID = 'console-ai-code-completion-citations-tooltip';
|
274
275
|
|
275
276
|
export class ConsoleView extends UI.Widget.VBox implements
|
@@ -624,8 +625,11 @@ export class ConsoleView extends UI.Widget.VBox implements
|
|
624
625
|
}
|
625
626
|
|
626
627
|
createAiCodeCompletionSummaryToolbar(): void {
|
627
|
-
this.aiCodeCompletionSummaryToolbar = new AiCodeCompletionSummaryToolbar(
|
628
|
-
|
628
|
+
this.aiCodeCompletionSummaryToolbar = new AiCodeCompletionSummaryToolbar({
|
629
|
+
citationsTooltipId: CITATIONS_TOOLTIP_ID,
|
630
|
+
disclaimerTooltipId: DISCLAIMER_TOOLTIP_ID,
|
631
|
+
spinnerTooltipId: SPINNER_TOOLTIP_ID
|
632
|
+
});
|
629
633
|
this.aiCodeCompletionSummaryToolbarContainer = this.element.createChild('div');
|
630
634
|
this.aiCodeCompletionSummaryToolbar.show(this.aiCodeCompletionSummaryToolbarContainer, undefined, true);
|
631
635
|
}
|
@@ -774,6 +774,10 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
|
|
774
774
|
this.#domTreeWidget.selectDOMNode(node, focus);
|
775
775
|
}
|
776
776
|
|
777
|
+
highlightNodeAttribute(node: SDK.DOMModel.DOMNode, attribute: string): void {
|
778
|
+
this.#domTreeWidget.highlightNodeAttribute(node, attribute);
|
779
|
+
}
|
780
|
+
|
777
781
|
selectAndShowSidebarTab(tabId: SidebarPaneTabId): void {
|
778
782
|
if (!this.sidebarPaneView) {
|
779
783
|
return;
|
@@ -488,6 +488,24 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
488
488
|
}
|
489
489
|
}
|
490
490
|
|
491
|
+
highlightAttribute(attributeName: string): void {
|
492
|
+
// If the attribute is not found, we highlight the tag name instead.
|
493
|
+
let animationElement = this.listItemElement.querySelector('.webkit-html-tag-name') ?? this.listItemElement;
|
494
|
+
|
495
|
+
if (this.nodeInternal.getAttribute(attributeName) !== undefined) {
|
496
|
+
const tag = this.listItemElement.getElementsByClassName('webkit-html-tag')[0];
|
497
|
+
const attributes = tag.getElementsByClassName('webkit-html-attribute');
|
498
|
+
for (const attribute of attributes) {
|
499
|
+
const attributeElement = attribute.getElementsByClassName('webkit-html-attribute-name')[0];
|
500
|
+
if (attributeElement.textContent === attributeName) {
|
501
|
+
animationElement = attributeElement;
|
502
|
+
break;
|
503
|
+
}
|
504
|
+
}
|
505
|
+
}
|
506
|
+
UI.UIUtils.runCSSAnimationOnce(animationElement, 'dom-update-highlight');
|
507
|
+
}
|
508
|
+
|
491
509
|
isClosingTag(): boolean {
|
492
510
|
return !isOpeningTag(this.tagTypeContext);
|
493
511
|
}
|
@@ -260,6 +260,10 @@ export class DOMTreeWidget extends UI.Widget.Widget {
|
|
260
260
|
this.#viewOutput?.elementsTreeOutline?.selectDOMNode(node, focus);
|
261
261
|
}
|
262
262
|
|
263
|
+
highlightNodeAttribute(node: SDK.DOMModel.DOMNode, attribute: string): void {
|
264
|
+
this.#viewOutput?.elementsTreeOutline?.highlightNodeAttribute(node, attribute);
|
265
|
+
}
|
266
|
+
|
263
267
|
setWordWrap(wrap: boolean): void {
|
264
268
|
this.#wrap = wrap;
|
265
269
|
this.performUpdate();
|
@@ -1006,6 +1010,15 @@ export class ElementsTreeOutline extends
|
|
1006
1010
|
treeElement.revealAndSelect(omitFocus);
|
1007
1011
|
}
|
1008
1012
|
|
1013
|
+
highlightNodeAttribute(node: SDK.DOMModel.DOMNode, attribute: string): void {
|
1014
|
+
const treeElement = this.findTreeElement(node);
|
1015
|
+
if (!treeElement) {
|
1016
|
+
return;
|
1017
|
+
}
|
1018
|
+
treeElement.reveal();
|
1019
|
+
treeElement.highlightAttribute(attribute);
|
1020
|
+
}
|
1021
|
+
|
1009
1022
|
treeElementFromEventInternal(event: MouseEvent): UI.TreeOutline.TreeElement|null {
|
1010
1023
|
const scrollContainer = this.element.parentElement;
|
1011
1024
|
if (!scrollContainer) {
|