chrome-devtools-frontend 1.0.1578486 → 1.0.1579812
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/cookbook/devtools_on_devtools.md +2 -2
- package/docs/feature-specs/elements-gutter-decorators.md +40 -0
- package/docs/feature-specs/elements-tree-selection-and-hover.md +31 -0
- package/docs/feature-specs/images/elements-gutter-decorators-multiple.png +0 -0
- package/docs/get_the_code.md +3 -3
- package/front_end/core/common/Revealer.ts +0 -5
- package/front_end/core/sdk/ConsoleModel.ts +0 -3
- package/front_end/generated/ARIAProperties.js +0 -6
- package/front_end/generated/InspectorBackendCommands.ts +1 -1
- package/front_end/generated/protocol.ts +1 -0
- package/front_end/models/ai_assistance/AiConversation.ts +42 -22
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +30 -0
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +79 -0
- package/front_end/models/issues_manager/CookieIssue.ts +0 -1
- package/front_end/models/issues_manager/IssuesManager.ts +2 -15
- package/front_end/models/javascript_metadata/NativeFunctions.js +4 -0
- package/front_end/panels/accessibility/AXBreadcrumbsPane.ts +5 -3
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +33 -27
- package/front_end/panels/ai_assistance/ai_assistance-meta.ts +0 -1
- package/front_end/panels/ai_assistance/components/ChatInput.ts +38 -18
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +7 -4
- package/front_end/panels/ai_assistance/components/ChatView.ts +2 -0
- package/front_end/panels/ai_assistance/components/chatInput.css +10 -3
- package/front_end/panels/animation/AnimationGroupPreviewUI.ts +1 -1
- package/front_end/panels/application/DeviceBoundSessionsView.ts +51 -76
- package/front_end/panels/application/ServiceWorkerUpdateCycleView.ts +1 -0
- package/front_end/panels/application/components/BackForwardCacheView.ts +4 -2
- package/front_end/panels/application/preloading/components/RuleSetDetailsView.css +0 -5
- package/front_end/panels/autofill/AutofillView.ts +1 -1
- package/front_end/panels/browser_debugger/XHRBreakpointsSidebarPane.ts +1 -0
- package/front_end/panels/common/AiCodeGenerationTeaser.ts +2 -2
- package/front_end/panels/common/AiCodeGenerationUpgradeDialog.ts +4 -4
- package/front_end/panels/console/ConsoleViewMessage.ts +1 -32
- package/front_end/panels/console/consoleView.css +0 -5
- package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +78 -0
- package/front_end/panels/elements/ElementsTreeElement.ts +93 -121
- package/front_end/panels/elements/ElementsTreeOutline.ts +1 -0
- package/front_end/panels/elements/LayoutPane.ts +8 -7
- package/front_end/panels/elements/components/ElementsBreadcrumbs.ts +1 -1
- package/front_end/panels/elements/components/StylePropertyEditor.ts +2 -1
- package/front_end/panels/elements/elements.ts +3 -0
- package/front_end/panels/elements/elementsTreeOutline.css +16 -5
- package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +1 -1
- package/front_end/panels/lighthouse/LighthouseStartView.ts +1 -1
- package/front_end/panels/lighthouse/lighthouseStartView.css +6 -0
- package/front_end/panels/linear_memory_inspector/components/LinearMemoryValueInterpreter.ts +1 -1
- package/front_end/panels/linear_memory_inspector/components/ValueInterpreterDisplay.ts +1 -1
- package/front_end/panels/media/PlayerListView.ts +14 -2
- package/front_end/panels/media/playerListView.css +5 -0
- package/front_end/panels/network/NetworkItemView.ts +2 -1
- package/front_end/panels/network/NetworkLogView.ts +2 -5
- package/front_end/panels/network/NetworkLogViewColumns.ts +19 -7
- package/front_end/panels/network/RequestInitiatorView.ts +20 -4
- package/front_end/panels/network/RequestPayloadView.ts +253 -280
- package/front_end/panels/network/ShowMoreDetailsWidget.ts +3 -3
- package/front_end/panels/network/components/RequestHeaderSection.ts +1 -1
- package/front_end/panels/network/requestPayloadTree.css +6 -3
- package/front_end/panels/network/requestPayloadView.css +1 -0
- package/front_end/panels/protocol_monitor/JSONEditor.ts +1 -1
- package/front_end/panels/recorder/components/RecordingListView.ts +1 -1
- package/front_end/panels/recorder/components/StepEditor.ts +3 -3
- package/front_end/panels/security/SecurityPanel.ts +0 -16
- package/front_end/panels/security/security-meta.ts +1 -15
- package/front_end/panels/settings/EditFileSystemView.ts +8 -8
- package/front_end/panels/settings/KeybindsSettingsTab.ts +2 -1
- package/front_end/panels/settings/SettingsScreen.ts +4 -4
- package/front_end/panels/sources/CallStackSidebarPane.ts +1 -1
- package/front_end/panels/sources/components/HeadersView.ts +2 -2
- package/front_end/panels/timeline/components/BreadcrumbsUI.ts +1 -1
- package/front_end/panels/timeline/components/SidebarAnnotationsTab.ts +1 -1
- package/front_end/panels/timeline/overlays/OverlaysImpl.ts +47 -26
- package/front_end/panels/timeline/overlays/components/TimespanBreakdownOverlay.ts +121 -39
- package/front_end/panels/timeline/overlays/components/timespanBreakdownOverlay.css +106 -101
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/buttons/Button.ts +1 -1
- package/front_end/ui/components/suggestion_input/SuggestionInput.ts +7 -12
- package/front_end/ui/components/tree_outline/TreeOutline.ts +1 -1
- package/front_end/ui/legacy/InplaceEditor.ts +1 -1
- package/front_end/ui/legacy/InspectorView.ts +6 -5
- package/front_end/ui/legacy/ListControl.ts +2 -1
- package/front_end/ui/legacy/ListWidget.ts +1 -1
- package/front_end/ui/legacy/SoftContextMenu.ts +2 -1
- package/front_end/ui/legacy/Treeoutline.ts +82 -22
- package/front_end/ui/legacy/components/data_grid/DataGrid.ts +4 -2
- package/front_end/ui/legacy/components/data_grid/SortableDataGrid.ts +59 -9
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +28 -15
- package/front_end/ui/legacy/components/object_ui/objectPropertiesSection.css +2 -1
- package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +2 -2
- package/front_end/ui/legacy/components/quick_open/FilteredListWidget.ts +1 -1
- package/front_end/ui/legacy/components/settings_ui/SettingsUI.ts +3 -3
- package/front_end/ui/legacy/components/source_frame/JSONView.ts +2 -3
- package/front_end/ui/legacy/softDropDown.css +7 -1
- package/front_end/ui/visual_logging/Debugging.ts +1 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
- package/mcp/mcp.ts +2 -0
- package/package.json +1 -1
|
@@ -38,7 +38,6 @@ import * as Common from '../../core/common/common.js';
|
|
|
38
38
|
import * as Host from '../../core/host/host.js';
|
|
39
39
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
40
40
|
import * as Platform from '../../core/platform/platform.js';
|
|
41
|
-
import * as Root from '../../core/root/root.js';
|
|
42
41
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
43
42
|
import * as Protocol from '../../generated/protocol.js';
|
|
44
43
|
import * as AiAssistanceModel from '../../models/ai_assistance/ai_assistance.js';
|
|
@@ -47,7 +46,6 @@ import type * as IssuesManager from '../../models/issues_manager/issues_manager.
|
|
|
47
46
|
import * as Logs from '../../models/logs/logs.js';
|
|
48
47
|
import * as TextUtils from '../../models/text_utils/text_utils.js';
|
|
49
48
|
import * as Workspace from '../../models/workspace/workspace.js';
|
|
50
|
-
import * as Buttons from '../../ui/components/buttons/buttons.js';
|
|
51
49
|
import * as CodeHighlighter from '../../ui/components/code_highlighter/code_highlighter.js';
|
|
52
50
|
import * as Highlighting from '../../ui/components/highlighting/highlighting.js';
|
|
53
51
|
import * as IssueCounter from '../../ui/components/issue_counter/issue_counter.js';
|
|
@@ -61,7 +59,6 @@ import * as Components from '../../ui/legacy/components/utils/utils.js';
|
|
|
61
59
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
62
60
|
import {render} from '../../ui/lit/lit.js';
|
|
63
61
|
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
64
|
-
import * as Security from '../security/security.js';
|
|
65
62
|
|
|
66
63
|
import {format, updateStyle} from './ConsoleFormat.js';
|
|
67
64
|
import {ConsoleInsightTeaser} from './ConsoleInsightTeaser.js';
|
|
@@ -220,10 +217,6 @@ const UIStrings = {
|
|
|
220
217
|
* @description Message to offer insights for a console message
|
|
221
218
|
*/
|
|
222
219
|
explainThisMessageWithAI: 'Understand this message. Powered by AI',
|
|
223
|
-
/**
|
|
224
|
-
* @description Tooltip shown when user hovers over the cookie icon to explain that the button will bring the user to the cookie report
|
|
225
|
-
*/
|
|
226
|
-
SeeIssueInCookieReport: 'Click to open privacy and security panel and show third-party cookie report',
|
|
227
220
|
/**
|
|
228
221
|
* @description Element text content in Object Properties Section
|
|
229
222
|
*/
|
|
@@ -574,23 +567,6 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
|
|
|
574
567
|
return elements;
|
|
575
568
|
}
|
|
576
569
|
|
|
577
|
-
#appendCookieReportButtonToElem(elem: HTMLElement): void {
|
|
578
|
-
const button = new Buttons.Button.Button();
|
|
579
|
-
button.data = {
|
|
580
|
-
size: Buttons.Button.Size.SMALL,
|
|
581
|
-
variant: Buttons.Button.Variant.ICON,
|
|
582
|
-
iconName: 'cookie',
|
|
583
|
-
jslogContext: 'privacy',
|
|
584
|
-
title: i18nString(UIStrings.SeeIssueInCookieReport)
|
|
585
|
-
};
|
|
586
|
-
|
|
587
|
-
button.addEventListener('click', () => {
|
|
588
|
-
void Common.Revealer.reveal(new Security.CookieReportView.CookieReportView());
|
|
589
|
-
});
|
|
590
|
-
|
|
591
|
-
elem.appendChild(button);
|
|
592
|
-
}
|
|
593
|
-
|
|
594
570
|
#getLinkifierMetric(): Host.UserMetrics.Action|undefined {
|
|
595
571
|
const request = Logs.NetworkLog.NetworkLog.requestForConsoleMessage(this.message);
|
|
596
572
|
if (request?.resourceType().isStyleSheet()) {
|
|
@@ -631,14 +607,6 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
|
|
|
631
607
|
return null;
|
|
632
608
|
};
|
|
633
609
|
|
|
634
|
-
if (this.message.isCookieReportIssue && Root.Runtime.hostConfig.devToolsPrivacyUI?.enabled) {
|
|
635
|
-
const anchorWrapperElement = document.createElement('span');
|
|
636
|
-
anchorWrapperElement.classList.add('console-message-anchor', 'cookie-report-anchor');
|
|
637
|
-
this.#appendCookieReportButtonToElem(anchorWrapperElement);
|
|
638
|
-
UI.UIUtils.createTextChild(anchorWrapperElement, ' ');
|
|
639
|
-
return anchorWrapperElement;
|
|
640
|
-
}
|
|
641
|
-
|
|
642
610
|
const anchorElement = linkify(this.message);
|
|
643
611
|
// Append a space to prevent the anchor text from being glued to the console message when the user selects and copies the console messages.
|
|
644
612
|
if (anchorElement) {
|
|
@@ -1457,6 +1425,7 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
|
|
|
1457
1425
|
this.elementInternal.className = 'console-message-wrapper';
|
|
1458
1426
|
this.elementInternal.setAttribute('jslog', `${VisualLogging.item('console-message').track({
|
|
1459
1427
|
click: true,
|
|
1428
|
+
resize: true,
|
|
1460
1429
|
keydown: 'ArrowUp|ArrowDown|ArrowLeft|ArrowRight|Enter|Space|Home|End',
|
|
1461
1430
|
})}`);
|
|
1462
1431
|
this.elementInternal.removeChildren();
|
|
@@ -11,6 +11,8 @@ import * as UI from '../../ui/legacy/legacy.js';
|
|
|
11
11
|
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
12
12
|
import {PanelUtils} from '../utils/utils.js';
|
|
13
13
|
|
|
14
|
+
import type {EditorHandles} from './ElementsTreeElement.js';
|
|
15
|
+
|
|
14
16
|
export class AdoptedStyleSheetTreeElement extends UI.TreeOutline.TreeElement {
|
|
15
17
|
private eventListener: Common.EventTarget.EventDescriptor|null = null;
|
|
16
18
|
|
|
@@ -58,6 +60,8 @@ export class AdoptedStyleSheetTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
58
60
|
}
|
|
59
61
|
|
|
60
62
|
export class AdoptedStyleSheetContentsTreeElement extends UI.TreeOutline.TreeElement {
|
|
63
|
+
private editing: EditorHandles|null = null;
|
|
64
|
+
|
|
61
65
|
constructor(private readonly styleSheetHeader: SDK.CSSStyleSheetHeader.CSSStyleSheetHeader) {
|
|
62
66
|
super('');
|
|
63
67
|
}
|
|
@@ -69,6 +73,9 @@ export class AdoptedStyleSheetContentsTreeElement extends UI.TreeOutline.TreeEle
|
|
|
69
73
|
}
|
|
70
74
|
|
|
71
75
|
override onunbind(): void {
|
|
76
|
+
if (this.editing) {
|
|
77
|
+
this.editing.cancel();
|
|
78
|
+
}
|
|
72
79
|
this.styleSheetHeader.cssModel().removeEventListener(
|
|
73
80
|
SDK.CSSModel.Events.StyleSheetChanged, this.onStyleSheetChanged, this);
|
|
74
81
|
}
|
|
@@ -91,4 +98,75 @@ export class AdoptedStyleSheetContentsTreeElement extends UI.TreeOutline.TreeEle
|
|
|
91
98
|
void this.onpopulate();
|
|
92
99
|
}
|
|
93
100
|
}
|
|
101
|
+
|
|
102
|
+
override ondblclick(event: Event): boolean {
|
|
103
|
+
if (this.editing) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
void this.startEditing(event.target as Element);
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
override onenter(): boolean {
|
|
111
|
+
if (this.editing) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
const target = this.listItemElement.querySelector('.webkit-html-text-node');
|
|
115
|
+
if (target) {
|
|
116
|
+
void this.startEditing(target);
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
private async startEditing(target: Element): Promise<void> {
|
|
123
|
+
if (this.editing || UI.UIUtils.isBeingEdited(target)) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const textNode = target.enclosingNodeOrSelfWithClass('webkit-html-text-node');
|
|
128
|
+
if (!textNode) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const data = await this.styleSheetHeader.requestContentData();
|
|
133
|
+
textNode.textContent = (TextUtils.ContentData.ContentData.isError(data) || !data.isTextContent) ? '' : data.text;
|
|
134
|
+
|
|
135
|
+
const config =
|
|
136
|
+
new UI.InplaceEditor.Config(this.editingCommitted.bind(this), () => this.editingCancelled(), undefined);
|
|
137
|
+
|
|
138
|
+
const editorHandles = UI.InplaceEditor.InplaceEditor.startEditing(textNode, config);
|
|
139
|
+
if (!editorHandles) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
this.editing = {
|
|
144
|
+
commit: editorHandles.commit,
|
|
145
|
+
cancel: editorHandles.cancel,
|
|
146
|
+
editor: undefined,
|
|
147
|
+
resize: () => {},
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const componentSelection = this.listItemElement.getComponentSelection();
|
|
151
|
+
componentSelection?.selectAllChildren(textNode);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private async editingCommitted(element: Element, newText: string, oldText: string|null): Promise<void> {
|
|
155
|
+
this.editing = null;
|
|
156
|
+
|
|
157
|
+
if (newText !== oldText) {
|
|
158
|
+
await this.styleSheetHeader.cssModel().setStyleSheetText(this.styleSheetHeader.id, newText, false);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
this.editingCancelled();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private editingCancelled(): void {
|
|
165
|
+
this.editing = null;
|
|
166
|
+
void this.onpopulate();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
isEditing(): boolean {
|
|
170
|
+
return this.editing !== null;
|
|
171
|
+
}
|
|
94
172
|
}
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
import '../../ui/components/adorners/adorners.js';
|
|
37
|
+
import '../../ui/components/buttons/buttons.js';
|
|
37
38
|
|
|
38
39
|
import * as Common from '../../core/common/common.js';
|
|
39
40
|
import * as Host from '../../core/host/host.js';
|
|
@@ -50,7 +51,6 @@ import * as TextUtils from '../../models/text_utils/text_utils.js';
|
|
|
50
51
|
import * as Workspace from '../../models/workspace/workspace.js';
|
|
51
52
|
import * as CodeMirror from '../../third_party/codemirror.next/codemirror.next.js';
|
|
52
53
|
import type * as Adorners from '../../ui/components/adorners/adorners.js';
|
|
53
|
-
import * as Buttons from '../../ui/components/buttons/buttons.js';
|
|
54
54
|
import * as CodeHighlighter from '../../ui/components/code_highlighter/code_highlighter.js';
|
|
55
55
|
import * as Highlighting from '../../ui/components/highlighting/highlighting.js';
|
|
56
56
|
import * as TextEditor from '../../ui/components/text_editor/text_editor.js';
|
|
@@ -417,11 +417,20 @@ export interface ViewInput {
|
|
|
417
417
|
showStartingStyleAdorner: boolean;
|
|
418
418
|
startingStyleAdornerActive: boolean;
|
|
419
419
|
onStartingStyleAdornerClick: (e: Event) => void;
|
|
420
|
+
|
|
421
|
+
isHovered: boolean;
|
|
422
|
+
isSelected: boolean;
|
|
423
|
+
showAiButton: boolean;
|
|
424
|
+
aiButtonTitle?: string;
|
|
425
|
+
onAiButtonClick: (e: Event) => void;
|
|
426
|
+
|
|
427
|
+
decorations: Decoration[];
|
|
428
|
+
descendantDecorations: Decoration[];
|
|
429
|
+
decorationsTooltip: string;
|
|
430
|
+
indent: number;
|
|
420
431
|
}
|
|
421
432
|
|
|
422
433
|
export interface ViewOutput {
|
|
423
|
-
gutterContainer?: HTMLElement;
|
|
424
|
-
decorationsElement?: HTMLElement;
|
|
425
434
|
contentElement?: HTMLElement;
|
|
426
435
|
}
|
|
427
436
|
|
|
@@ -443,6 +452,11 @@ export function adornerRef(): DirectiveResult<typeof Lit.Directives.RefDirective
|
|
|
443
452
|
});
|
|
444
453
|
}
|
|
445
454
|
|
|
455
|
+
export interface Decoration {
|
|
456
|
+
title: string;
|
|
457
|
+
color: string;
|
|
458
|
+
}
|
|
459
|
+
|
|
446
460
|
function handleAdornerKeydown(cb: (event: Event) => void): (event: KeyboardEvent) => void {
|
|
447
461
|
return (event: KeyboardEvent) => {
|
|
448
462
|
if (event.code === 'Enter' || event.code === 'Space') {
|
|
@@ -458,13 +472,27 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
|
|
|
458
472
|
input.showGridAdorner || input.showGridLanesAdorner || input.showMediaAdorner || input.showPopoverAdorner ||
|
|
459
473
|
input.showTopLayerAdorner || input.showViewSourceAdorner || input.showScrollAdorner ||
|
|
460
474
|
input.showScrollSnapAdorner || input.showSlotAdorner || input.showStartingStyleAdorner;
|
|
475
|
+
const gutterContainerClasses = {
|
|
476
|
+
'has-decorations': input.decorations.length || input.descendantDecorations.length,
|
|
477
|
+
'gutter-container': true,
|
|
478
|
+
};
|
|
461
479
|
// clang-format off
|
|
462
480
|
render(html`
|
|
463
481
|
<div ${ref(el => { output.contentElement = el as HTMLElement; })}>
|
|
464
482
|
${input.nodeInfo ? html`<span class="highlight">${input.nodeInfo}</span>` : nothing}
|
|
465
|
-
|
|
483
|
+
${input.isHovered || input.isSelected ? html`
|
|
484
|
+
<div class="selection fill" style=${`margin-left: ${-input.indent}px`}></div>
|
|
485
|
+
` : nothing}
|
|
486
|
+
<div class=${Lit.Directives.classMap(gutterContainerClasses)}
|
|
487
|
+
style="left: ${-input.indent}px"
|
|
488
|
+
@click=${input.onGutterClick}>
|
|
466
489
|
<devtools-icon name="dots-horizontal"></devtools-icon>
|
|
467
|
-
|
|
490
|
+
${input.decorations.length || input.descendantDecorations.length ? html`
|
|
491
|
+
<div class="elements-gutter-decoration-container"
|
|
492
|
+
title=${input.decorationsTooltip}>
|
|
493
|
+
${input.decorations.map(d => html`<div class="elements-gutter-decoration" style="--decoration-color: ${d.color}"></div>`)}
|
|
494
|
+
${input.descendantDecorations.map(d => html`<div class="elements-gutter-decoration elements-has-decorated-children" style="--decoration-color: ${d.color}"></div>`)}
|
|
495
|
+
</div>` : nothing}
|
|
468
496
|
</div>
|
|
469
497
|
${hasAdorners ? html`<div class="adorner-container ${!hasAdorners ? 'hidden' : ''}">
|
|
470
498
|
${input.showAdAdorner ? html`<devtools-adorner
|
|
@@ -630,6 +658,20 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
|
|
|
630
658
|
<span>${ElementsComponents.AdornerManager.RegisteredAdorners.SCROLL_SNAP}</span>
|
|
631
659
|
</devtools-adorner>` : nothing}
|
|
632
660
|
</div>`: nothing}
|
|
661
|
+
${input.isSelected ? html`
|
|
662
|
+
<span class="selected-hint" title=${i18nString(UIStrings.useSInTheConsoleToReferToThis, {PH1: '$0'})} aria-hidden="true"></span>
|
|
663
|
+
` : nothing}
|
|
664
|
+
${input.showAiButton ? html`
|
|
665
|
+
<span class="ai-button-container">
|
|
666
|
+
<devtools-floating-button
|
|
667
|
+
icon-name=${AIAssistance.AiUtils.getIconName()}
|
|
668
|
+
title=${input.aiButtonTitle || ''}
|
|
669
|
+
jslogcontext="ask-ai"
|
|
670
|
+
@click=${input.onAiButtonClick}
|
|
671
|
+
@mousedown=${(e: Event) => e.stopPropagation()}>
|
|
672
|
+
</devtools-floating-button>
|
|
673
|
+
</span>
|
|
674
|
+
` : nothing}
|
|
633
675
|
</div>
|
|
634
676
|
`, target);
|
|
635
677
|
// clang-format on
|
|
@@ -640,8 +682,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
640
682
|
override treeOutline: ElementsTreeOutline|null;
|
|
641
683
|
|
|
642
684
|
// Handled by the view output for now.
|
|
643
|
-
gutterContainer!: HTMLElement;
|
|
644
|
-
decorationsElement!: HTMLElement;
|
|
645
685
|
contentElement!: HTMLElement;
|
|
646
686
|
|
|
647
687
|
private searchQuery: string|null;
|
|
@@ -652,9 +692,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
652
692
|
private editing: EditorHandles|null;
|
|
653
693
|
private htmlEditElement?: HTMLElement;
|
|
654
694
|
expandAllButtonElement: UI.TreeOutline.TreeElement|null;
|
|
655
|
-
selectionElement?: HTMLDivElement;
|
|
656
|
-
private hintElement?: HTMLElement;
|
|
657
|
-
private aiButtonContainer?: HTMLElement;
|
|
658
695
|
#elementIssues = new Map<string, IssuesManager.Issue.Issue>();
|
|
659
696
|
#nodeElementToIssue = new Map<Element, IssuesManager.Issue.Issue[]>();
|
|
660
697
|
#highlights: Range[] = [];
|
|
@@ -672,6 +709,10 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
672
709
|
#startingStyleAdornerActive = false;
|
|
673
710
|
#layout: SDK.CSSModel.LayoutProperties|null = null;
|
|
674
711
|
|
|
712
|
+
#decorations: Decoration[] = [];
|
|
713
|
+
#descendantDecorations: Decoration[] = [];
|
|
714
|
+
#decorationsTooltip = '';
|
|
715
|
+
|
|
675
716
|
constructor(node: SDK.DOMModel.DOMNode, isClosingTag?: boolean) {
|
|
676
717
|
// The title will be updated in onattach.
|
|
677
718
|
super();
|
|
@@ -680,6 +721,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
680
721
|
this.listItemElement.setAttribute(
|
|
681
722
|
'jslog', `${VisualLogging.treeItem().parent('elementsTreeOutline').track({
|
|
682
723
|
keydown: 'ArrowUp|ArrowDown|ArrowLeft|ArrowRight|Backspace|Delete|Enter|Space|Home|End',
|
|
724
|
+
resize: true,
|
|
683
725
|
drag: true,
|
|
684
726
|
click: true,
|
|
685
727
|
})}`);
|
|
@@ -797,6 +839,10 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
797
839
|
showScrollAdorner: ((this.node().nodeName() === 'HTML' && this.node().ownerDocument?.isScrollable()) ||
|
|
798
840
|
(this.node().nodeName() !== '#document' && this.node().isScrollable())) &&
|
|
799
841
|
!this.isClosingTag(),
|
|
842
|
+
decorations: this.#decorations,
|
|
843
|
+
descendantDecorations: this.expanded ? [] : this.#descendantDecorations,
|
|
844
|
+
decorationsTooltip: this.#decorationsTooltip,
|
|
845
|
+
indent: this.computeLeftIndent(),
|
|
800
846
|
showScrollSnapAdorner: Boolean(this.#layout?.hasScroll) && !this.isClosingTag(),
|
|
801
847
|
scrollSnapAdornerActive: this.#scrollSnapAdornerActive,
|
|
802
848
|
showSlotAdorner: Boolean(this.nodeInternal.assignedSlot) && !this.isClosingTag(),
|
|
@@ -827,6 +873,21 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
827
873
|
}
|
|
828
874
|
this.treeOutline.revealInTopLayer(this.node());
|
|
829
875
|
},
|
|
876
|
+
isHovered: this.#hovered,
|
|
877
|
+
isSelected: this.selected,
|
|
878
|
+
showAiButton: Boolean(this.#hovered || this.selected) && this.node().nodeType() === Node.ELEMENT_NODE &&
|
|
879
|
+
UI.ActionRegistry.ActionRegistry.instance().hasAction('freestyler.elements-floating-button'),
|
|
880
|
+
aiButtonTitle: UI.ActionRegistry.ActionRegistry.instance().hasAction('freestyler.elements-floating-button') ?
|
|
881
|
+
UI.ActionRegistry.ActionRegistry.instance().getAction('freestyler.elements-floating-button').title() :
|
|
882
|
+
undefined,
|
|
883
|
+
onAiButtonClick: (ev: Event) => {
|
|
884
|
+
ev.stopPropagation();
|
|
885
|
+
this.select(true, false);
|
|
886
|
+
const action = UI.ActionRegistry.ActionRegistry.instance().getAction('freestyler.elements-floating-button');
|
|
887
|
+
if (action) {
|
|
888
|
+
void action.execute();
|
|
889
|
+
}
|
|
890
|
+
},
|
|
830
891
|
},
|
|
831
892
|
this, this.listItemElement);
|
|
832
893
|
}
|
|
@@ -962,22 +1023,15 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
962
1023
|
return;
|
|
963
1024
|
}
|
|
964
1025
|
|
|
965
|
-
if (isHovered && !this.aiButtonContainer) {
|
|
966
|
-
this.createAiButton();
|
|
967
|
-
} else if (!isHovered && this.aiButtonContainer) {
|
|
968
|
-
this.aiButtonContainer.remove();
|
|
969
|
-
delete this.aiButtonContainer;
|
|
970
|
-
}
|
|
971
|
-
|
|
972
1026
|
this.#hovered = isHovered;
|
|
973
1027
|
|
|
974
1028
|
if (this.listItemElement) {
|
|
975
1029
|
if (isHovered) {
|
|
976
|
-
this.createSelection();
|
|
977
1030
|
this.listItemElement.classList.add('hovered');
|
|
978
1031
|
} else {
|
|
979
1032
|
this.listItemElement.classList.remove('hovered');
|
|
980
1033
|
}
|
|
1034
|
+
this.performUpdate();
|
|
981
1035
|
}
|
|
982
1036
|
}
|
|
983
1037
|
|
|
@@ -1043,54 +1097,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1043
1097
|
this.#expandedChildrenLimit = expandedChildrenLimit;
|
|
1044
1098
|
}
|
|
1045
1099
|
|
|
1046
|
-
private createSelection(): void {
|
|
1047
|
-
const contentElement = this.contentElement;
|
|
1048
|
-
if (!contentElement) {
|
|
1049
|
-
return;
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
if (!this.selectionElement) {
|
|
1053
|
-
this.selectionElement = document.createElement('div');
|
|
1054
|
-
this.selectionElement.className = 'selection fill';
|
|
1055
|
-
this.selectionElement.style.setProperty('margin-left', (-this.computeLeftIndent()) + 'px');
|
|
1056
|
-
contentElement.prepend(this.selectionElement);
|
|
1057
|
-
}
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
private createHint(): void {
|
|
1061
|
-
if (this.contentElement && !this.hintElement) {
|
|
1062
|
-
this.hintElement = this.contentElement.createChild('span', 'selected-hint');
|
|
1063
|
-
const selectedElementCommand = '$0';
|
|
1064
|
-
UI.Tooltip.Tooltip.install(
|
|
1065
|
-
this.hintElement, i18nString(UIStrings.useSInTheConsoleToReferToThis, {PH1: selectedElementCommand}));
|
|
1066
|
-
UI.ARIAUtils.setHidden(this.hintElement, true);
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
private createAiButton(): void {
|
|
1071
|
-
const isElementNode = this.node().nodeType() === Node.ELEMENT_NODE;
|
|
1072
|
-
if (!isElementNode ||
|
|
1073
|
-
!UI.ActionRegistry.ActionRegistry.instance().hasAction('freestyler.elements-floating-button')) {
|
|
1074
|
-
return;
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
const action = UI.ActionRegistry.ActionRegistry.instance().getAction('freestyler.elements-floating-button');
|
|
1078
|
-
if (this.contentElement && !this.aiButtonContainer) {
|
|
1079
|
-
this.aiButtonContainer = this.contentElement.createChild('span', 'ai-button-container');
|
|
1080
|
-
const floatingButton =
|
|
1081
|
-
Buttons.FloatingButton.create(AIAssistance.AiUtils.getIconName(), action.title(), 'ask-ai');
|
|
1082
|
-
floatingButton.addEventListener('click', ev => {
|
|
1083
|
-
ev.stopPropagation();
|
|
1084
|
-
this.select(true, false);
|
|
1085
|
-
void action.execute();
|
|
1086
|
-
}, {capture: true});
|
|
1087
|
-
floatingButton.addEventListener('mousedown', ev => {
|
|
1088
|
-
ev.stopPropagation();
|
|
1089
|
-
}, {capture: true});
|
|
1090
|
-
this.aiButtonContainer.appendChild(floatingButton);
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
1100
|
override onbind(): void {
|
|
1095
1101
|
this.performUpdate();
|
|
1096
1102
|
if (this.treeOutline && !this.isClosingTag()) {
|
|
@@ -1156,6 +1162,14 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1156
1162
|
onPopoverAdornerClick: () => {},
|
|
1157
1163
|
onScrollSnapAdornerClick: () => {},
|
|
1158
1164
|
onTopLayerAdornerClick: () => {},
|
|
1165
|
+
isHovered: false,
|
|
1166
|
+
isSelected: false,
|
|
1167
|
+
showAiButton: false,
|
|
1168
|
+
onAiButtonClick: () => {},
|
|
1169
|
+
decorations: [],
|
|
1170
|
+
descendantDecorations: [],
|
|
1171
|
+
decorationsTooltip: '',
|
|
1172
|
+
indent: 0,
|
|
1159
1173
|
},
|
|
1160
1174
|
this, this.listItemElement);
|
|
1161
1175
|
|
|
@@ -1219,8 +1233,8 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1219
1233
|
|
|
1220
1234
|
override onattach(): void {
|
|
1221
1235
|
if (this.#hovered) {
|
|
1222
|
-
this.createSelection();
|
|
1223
1236
|
this.listItemElement.classList.add('hovered');
|
|
1237
|
+
this.performUpdate();
|
|
1224
1238
|
}
|
|
1225
1239
|
|
|
1226
1240
|
this.updateTitle();
|
|
@@ -1278,8 +1292,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
1278
1292
|
this.nodeInternal.highlight();
|
|
1279
1293
|
Host.userMetrics.actionTaken(Host.UserMetrics.Action.ChangeInspectedNodeInElementsPanel);
|
|
1280
1294
|
}
|
|
1281
|
-
this.
|
|
1282
|
-
this.createHint();
|
|
1295
|
+
this.performUpdate();
|
|
1283
1296
|
this.treeOutline.suppressRevealAndSelect = false;
|
|
1284
1297
|
return true;
|
|
1285
1298
|
}
|
|
@@ -2252,10 +2265,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
2252
2265
|
// fixme: make it clear that `this.title = x` is a setter with significant side effects
|
|
2253
2266
|
this.title = this.contentElement;
|
|
2254
2267
|
this.updateDecorations();
|
|
2255
|
-
if (this.selected) {
|
|
2256
|
-
this.createSelection();
|
|
2257
|
-
this.createHint();
|
|
2258
|
-
}
|
|
2259
2268
|
|
|
2260
2269
|
// If there is an issue with this node, make sure to update it.
|
|
2261
2270
|
for (const issue of this.#elementIssues.values()) {
|
|
@@ -2278,10 +2287,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
2278
2287
|
}
|
|
2279
2288
|
|
|
2280
2289
|
updateDecorations(): void {
|
|
2281
|
-
const indent = this.computeLeftIndent();
|
|
2282
|
-
this.gutterContainer.style.left = (-indent) + 'px';
|
|
2283
|
-
this.listItemElement.style.setProperty('--indent', indent + 'px');
|
|
2284
|
-
|
|
2285
2290
|
if (this.isClosingTag()) {
|
|
2286
2291
|
return;
|
|
2287
2292
|
}
|
|
@@ -2339,60 +2344,27 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
|
|
|
2339
2344
|
return Promise.all(promises).then(updateDecorationsUI.bind(this));
|
|
2340
2345
|
|
|
2341
2346
|
function updateDecorationsUI(this: ElementsTreeElement): void {
|
|
2342
|
-
this
|
|
2343
|
-
this
|
|
2344
|
-
this.gutterContainer.classList.toggle(
|
|
2345
|
-
'has-decorations', Boolean(decorations.length || descendantDecorations.length));
|
|
2346
|
-
UI.ARIAUtils.setLabel(this.decorationsElement, '');
|
|
2347
|
+
this.#decorations = decorations;
|
|
2348
|
+
this.#descendantDecorations = descendantDecorations;
|
|
2347
2349
|
|
|
2348
2350
|
if (!decorations.length && !descendantDecorations.length) {
|
|
2351
|
+
this.#decorationsTooltip = '';
|
|
2352
|
+
this.performUpdate();
|
|
2349
2353
|
return;
|
|
2350
2354
|
}
|
|
2351
2355
|
|
|
2352
|
-
const
|
|
2353
|
-
const titles = document.createElement('div');
|
|
2354
|
-
|
|
2356
|
+
const tooltip: string[] = [];
|
|
2355
2357
|
for (const decoration of decorations) {
|
|
2356
|
-
|
|
2357
|
-
titleElement.textContent = decoration.title;
|
|
2358
|
-
colors.add(decoration.color);
|
|
2358
|
+
tooltip.push(decoration.title);
|
|
2359
2359
|
}
|
|
2360
|
-
if (this.expanded &&
|
|
2361
|
-
|
|
2362
|
-
}
|
|
2363
|
-
|
|
2364
|
-
const descendantColors = new Set<string>();
|
|
2365
|
-
if (descendantDecorations.length) {
|
|
2366
|
-
let element = titles.createChild('div');
|
|
2367
|
-
element.textContent = i18nString(UIStrings.children);
|
|
2360
|
+
if (!this.expanded && descendantDecorations.length) {
|
|
2361
|
+
tooltip.push(i18nString(UIStrings.children));
|
|
2368
2362
|
for (const decoration of descendantDecorations) {
|
|
2369
|
-
|
|
2370
|
-
element.style.marginLeft = '15px';
|
|
2371
|
-
element.textContent = decoration.title;
|
|
2372
|
-
descendantColors.add(decoration.color);
|
|
2373
|
-
}
|
|
2374
|
-
}
|
|
2375
|
-
|
|
2376
|
-
let offset = 0;
|
|
2377
|
-
processColors.call(this, colors, 'elements-gutter-decoration');
|
|
2378
|
-
if (!this.expanded) {
|
|
2379
|
-
processColors.call(this, descendantColors, 'elements-gutter-decoration elements-has-decorated-children');
|
|
2380
|
-
}
|
|
2381
|
-
UI.Tooltip.Tooltip.install(this.decorationsElement, titles.textContent);
|
|
2382
|
-
UI.ARIAUtils.setLabel(this.decorationsElement, titles.textContent || '');
|
|
2383
|
-
|
|
2384
|
-
function processColors(this: ElementsTreeElement, colors: Set<string>, className: string): void {
|
|
2385
|
-
for (const color of colors) {
|
|
2386
|
-
const child = this.decorationsElement.createChild('div', className);
|
|
2387
|
-
this.decorationsElement.classList.remove('hidden');
|
|
2388
|
-
child.style.backgroundColor = color;
|
|
2389
|
-
child.style.borderColor = color;
|
|
2390
|
-
if (offset) {
|
|
2391
|
-
child.style.marginLeft = offset + 'px';
|
|
2392
|
-
}
|
|
2393
|
-
offset += 3;
|
|
2363
|
+
tooltip.push(decoration.title);
|
|
2394
2364
|
}
|
|
2395
2365
|
}
|
|
2366
|
+
this.#decorationsTooltip = tooltip.join('\n');
|
|
2367
|
+
this.performUpdate();
|
|
2396
2368
|
}
|
|
2397
2369
|
}
|
|
2398
2370
|
|
|
@@ -83,6 +83,7 @@ const elementsTreeOutlineByDOMModel = new WeakMap<SDK.DOMModel.DOMModel, Element
|
|
|
83
83
|
const populatedTreeElements = new WeakSet<ElementsTreeElement>();
|
|
84
84
|
|
|
85
85
|
export type View = typeof DEFAULT_VIEW;
|
|
86
|
+
export {elementsTreeOutlineStyles};
|
|
86
87
|
|
|
87
88
|
interface ViewInput {
|
|
88
89
|
omitRootDOMNode: boolean;
|
|
@@ -228,26 +228,27 @@ const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
228
228
|
event.preventDefault();
|
|
229
229
|
}
|
|
230
230
|
};
|
|
231
|
+
// clang-format off
|
|
231
232
|
const renderElement = (element: LayoutElement): Lit.LitTemplate => html`<div
|
|
232
233
|
class="element"
|
|
233
|
-
jslog=${VisualLogging.item()}>
|
|
234
|
+
jslog=${VisualLogging.item().track({resize: true})}>
|
|
234
235
|
<devtools-checkbox
|
|
235
236
|
data-element="true"
|
|
236
237
|
class="checkbox-label"
|
|
237
238
|
.checked=${element.enabled}
|
|
238
239
|
@change=${(e: Event) => input.onElementToggle(element, e)}
|
|
239
|
-
jslog=${VisualLogging.toggle().track({
|
|
240
|
-
click: true
|
|
241
|
-
})}>
|
|
240
|
+
jslog=${VisualLogging.toggle().track({click: true, resize: true })}>
|
|
242
241
|
<span
|
|
243
242
|
class="node-text-container"
|
|
244
243
|
data-label="true"
|
|
245
244
|
@mouseenter=${(e: MouseEvent) => input.onMouseEnter(element, e)}
|
|
246
245
|
@mouseleave=${(e: MouseEvent) => input.onMouseLeave(element, e)}>
|
|
247
246
|
<devtools-node-text .data=${{
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
247
|
+
nodeId: element.domId,
|
|
248
|
+
nodeTitle: element.name,
|
|
249
|
+
nodeClasses: element.domClasses
|
|
250
|
+
}}>
|
|
251
|
+
</devtools-node-text>
|
|
251
252
|
</span>
|
|
252
253
|
</devtools-checkbox>
|
|
253
254
|
<label
|
|
@@ -303,7 +303,7 @@ export class ElementsBreadcrumbs extends HTMLElement {
|
|
|
303
303
|
data-crumb="true"
|
|
304
304
|
>
|
|
305
305
|
<a href="#" draggable=false class="crumb-link"
|
|
306
|
-
jslog=${VisualLogging.item().track({click:true})}
|
|
306
|
+
jslog=${VisualLogging.item().track({click:true, resize:true})}
|
|
307
307
|
@click=${this.#onCrumbClick(crumb.node)}
|
|
308
308
|
@mousemove=${this.#onCrumbMouseMove(crumb.node)}
|
|
309
309
|
@mouseleave=${this.#onCrumbMouseLeave(crumb.node)}
|
|
@@ -194,7 +194,8 @@ export class StylePropertyEditor extends HTMLElement {
|
|
|
194
194
|
return html`
|
|
195
195
|
<button title=${title}
|
|
196
196
|
class=${classes}
|
|
197
|
-
jslog=${
|
|
197
|
+
jslog=${
|
|
198
|
+
VisualLogging.item().track({click: true, resize: true}).context(`${propertyName}-${propertyValue}`)}
|
|
198
199
|
@click=${() => this.#onButtonClick(propertyName, propertyValue, selected)}>
|
|
199
200
|
<devtools-icon style=${transform} name=${iconInfo.iconName}>
|
|
200
201
|
</devtools-icon>
|
|
@@ -8,6 +8,7 @@ import './ComputedStyleModel.js';
|
|
|
8
8
|
import './DOMPath.js';
|
|
9
9
|
import './ElementsSidebarPane.js';
|
|
10
10
|
import './ElementsTreeElement.js';
|
|
11
|
+
import './AdoptedStyleSheetTreeElement.js';
|
|
11
12
|
import './TopLayerContainer.js';
|
|
12
13
|
import './ElementsTreeOutline.js';
|
|
13
14
|
import './EventListenersWidget.js';
|
|
@@ -30,6 +31,7 @@ import './AccessibilityTreeView.js';
|
|
|
30
31
|
|
|
31
32
|
import * as AccessibilityTreeUtils from './AccessibilityTreeUtils.js';
|
|
32
33
|
import * as AccessibilityTreeView from './AccessibilityTreeView.js';
|
|
34
|
+
import * as AdoptedStyleSheetTreeElement from './AdoptedStyleSheetTreeElement.js';
|
|
33
35
|
import * as ClassesPaneWidget from './ClassesPaneWidget.js';
|
|
34
36
|
import * as ColorSwatchPopoverIcon from './ColorSwatchPopoverIcon.js';
|
|
35
37
|
import * as ComputedStyleModel from './ComputedStyleModel.js';
|
|
@@ -65,6 +67,7 @@ import * as WebCustomData from './WebCustomData.js';
|
|
|
65
67
|
export {
|
|
66
68
|
AccessibilityTreeUtils,
|
|
67
69
|
AccessibilityTreeView,
|
|
70
|
+
AdoptedStyleSheetTreeElement,
|
|
68
71
|
ClassesPaneWidget,
|
|
69
72
|
ColorSwatchPopoverIcon,
|
|
70
73
|
ComputedStyleModel,
|