chrome-devtools-frontend 1.0.1515988 → 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.
Files changed (122) hide show
  1. package/docs/checklist/README.md +2 -2
  2. package/docs/checklist/javascript.md +1 -1
  3. package/docs/contributing/README.md +1 -1
  4. package/docs/contributing/settings-experiments-features.md +9 -8
  5. package/docs/cookbook/devtools_on_devtools.md +2 -2
  6. package/docs/cookbook/localization.md +10 -10
  7. package/docs/devtools-protocol.md +9 -8
  8. package/docs/ecosystem/automatic_workspace_folders.md +3 -3
  9. package/docs/get_the_code.md +0 -2
  10. package/docs/styleguide/ux/components.md +166 -85
  11. package/docs/styleguide/ux/numbers.md +3 -4
  12. package/front_end/core/common/README.md +13 -12
  13. package/front_end/core/host/GdpClient.ts +16 -1
  14. package/front_end/core/host/UserMetrics.ts +8 -2
  15. package/front_end/core/root/Runtime.ts +13 -0
  16. package/front_end/core/sdk/CSSMatchedStyles.ts +5 -1
  17. package/front_end/entrypoints/main/MainImpl.ts +6 -3
  18. package/front_end/generated/InspectorBackendCommands.js +10 -7
  19. package/front_end/generated/SupportedCSSProperties.js +21 -7
  20. package/front_end/generated/protocol-mapping.d.ts +16 -1
  21. package/front_end/generated/protocol-proxy-api.d.ts +13 -1
  22. package/front_end/generated/protocol.ts +95 -0
  23. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +170 -54
  24. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +14 -181
  25. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +13 -315
  26. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +224 -50
  27. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +310 -11
  28. package/front_end/models/ai_assistance/performance/AIContext.ts +15 -2
  29. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +41 -19
  30. package/front_end/models/badges/Badge.ts +8 -3
  31. package/front_end/models/badges/CodeWhispererBadge.ts +2 -4
  32. package/front_end/models/badges/StarterBadge.ts +2 -2
  33. package/front_end/models/badges/UserBadges.ts +59 -6
  34. package/front_end/models/formatter/FormatterWorkerPool.ts +3 -3
  35. package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
  36. package/front_end/models/trace/README.md +28 -1
  37. package/front_end/models/trace/handlers/UserTimingsHandler.ts +1 -1
  38. package/front_end/models/trace/helpers/Trace.ts +99 -43
  39. package/front_end/models/trace/types/TraceEvents.ts +9 -0
  40. package/front_end/panels/accessibility/ARIAAttributesView.ts +113 -191
  41. package/front_end/panels/accessibility/AccessibilityNodeView.ts +9 -9
  42. package/front_end/panels/accessibility/AccessibilitySubPane.ts +6 -4
  43. package/front_end/panels/accessibility/accessibilityProperties.css +2 -0
  44. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +16 -2
  45. package/front_end/panels/ai_assistance/components/ChatView.ts +9 -10
  46. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +42 -0
  47. package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +32 -9
  48. package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +7 -1
  49. package/front_end/panels/common/BadgeNotification.ts +67 -15
  50. package/front_end/panels/common/GdpSignUpDialog.ts +18 -9
  51. package/front_end/panels/console/ConsolePrompt.ts +1 -1
  52. package/front_end/panels/console/ConsoleView.ts +6 -2
  53. package/front_end/panels/elements/ComputedStyleWidget.ts +1 -2
  54. package/front_end/panels/elements/ElementsPanel.ts +4 -0
  55. package/front_end/panels/elements/ElementsTreeElement.ts +18 -0
  56. package/front_end/panels/elements/ElementsTreeOutline.ts +13 -0
  57. package/front_end/panels/elements/LayoutPane.ts +1 -1
  58. package/front_end/panels/elements/StylePropertyTreeElement.ts +21 -6
  59. package/front_end/panels/media/TickingFlameChart.ts +1 -1
  60. package/front_end/panels/network/NetworkLogView.ts +5 -1
  61. package/front_end/panels/profiler/HeapSnapshotView.ts +34 -19
  62. package/front_end/panels/search/SearchResultsPane.ts +126 -145
  63. package/front_end/panels/search/SearchView.ts +43 -59
  64. package/front_end/panels/settings/components/SyncSection.ts +16 -8
  65. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +6 -1
  66. package/front_end/panels/sources/OutlineQuickOpen.ts +3 -1
  67. package/front_end/panels/sources/SourcesPanel.ts +3 -0
  68. package/front_end/panels/timeline/AppenderUtils.ts +2 -2
  69. package/front_end/panels/timeline/ExtensionTrackAppender.ts +13 -4
  70. package/front_end/panels/timeline/GPUTrackAppender.ts +2 -1
  71. package/front_end/panels/timeline/InteractionsTrackAppender.ts +5 -1
  72. package/front_end/panels/timeline/LayoutShiftsTrackAppender.ts +2 -1
  73. package/front_end/panels/timeline/ThreadAppender.ts +12 -3
  74. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +9 -4
  75. package/front_end/panels/timeline/TimelinePanel.ts +3 -2
  76. package/front_end/panels/timeline/TimelineUIUtils.ts +18 -12
  77. package/front_end/panels/timeline/TimingsTrackAppender.ts +6 -1
  78. package/front_end/panels/timeline/components/CPUThrottlingSelector.ts +95 -82
  79. package/front_end/panels/timeline/components/LiveMetricsView.ts +2 -2
  80. package/front_end/panels/timeline/components/cpuThrottlingSelector.css +17 -15
  81. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +3 -0
  82. package/front_end/third_party/chromium/README.chromium +1 -1
  83. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  84. package/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -1
  85. package/front_end/third_party/codemirror.next/codemirror.next.d.ts +6 -9
  86. package/front_end/third_party/codemirror.next/package.json +2 -1
  87. package/front_end/third_party/diff/README.chromium +1 -0
  88. package/front_end/third_party/puppeteer/README.chromium +2 -2
  89. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
  90. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Accessibility.js +0 -20
  91. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Accessibility.js.map +1 -1
  92. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
  93. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +1 -1
  94. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  95. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +1 -1
  96. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +1 -1
  97. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  98. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  99. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +2 -23
  100. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Accessibility.js +0 -20
  101. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Accessibility.js.map +1 -1
  102. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
  103. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
  104. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +1 -1
  105. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +1 -1
  106. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  107. package/front_end/third_party/puppeteer/package/package.json +1 -1
  108. package/front_end/third_party/puppeteer/package/src/cdp/Accessibility.ts +1 -21
  109. package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
  110. package/front_end/third_party/puppeteer/package/src/revisions.ts +1 -1
  111. package/front_end/ui/components/text_editor/config.ts +36 -8
  112. package/front_end/ui/components/tooltips/Tooltip.ts +71 -34
  113. package/front_end/ui/legacy/README.md +33 -24
  114. package/front_end/ui/legacy/SearchableView.ts +19 -26
  115. package/front_end/ui/legacy/TextPrompt.ts +166 -1
  116. package/front_end/ui/legacy/Treeoutline.ts +16 -2
  117. package/front_end/ui/legacy/UIUtils.ts +15 -2
  118. package/front_end/ui/legacy/XElement.ts +0 -43
  119. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +20 -4
  120. package/front_end/ui/visual_logging/KnownContextValues.ts +24 -6
  121. package/front_end/ui/visual_logging/README.md +43 -27
  122. package/package.json +1 -1
@@ -1,14 +1,15 @@
1
1
  // Copyright 2016 The Chromium Authors
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
- /* eslint-disable rulesdir/no-imperative-dom-api */
5
4
 
6
5
  import * as i18n from '../../core/i18n/i18n.js';
7
6
  import * as Platform from '../../core/platform/platform.js';
8
7
  import * as SDK from '../../core/sdk/sdk.js';
9
8
  import * as UI from '../../ui/legacy/legacy.js';
9
+ import * as Lit from '../../ui/lit/lit.js';
10
10
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
11
11
 
12
+ import accessibilityPropertiesStyles from './accessibilityProperties.css.js';
12
13
  import {AccessibilitySubPane} from './AccessibilitySubPane.js';
13
14
  import {ariaMetadata} from './ARIAMetadata.js';
14
15
 
@@ -24,214 +25,135 @@ const UIStrings = {
24
25
  } as const;
25
26
  const str_ = i18n.i18n.registerUIStrings('panels/accessibility/ARIAAttributesView.ts', UIStrings);
26
27
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
28
+ const {render, html} = Lit;
29
+
30
+ interface ViewInput {
31
+ propertyCompletions: Map<SDK.DOMModel.Attribute, string[]>;
32
+ onStartEditing: (attribute: SDK.DOMModel.Attribute) => void;
33
+ onCommitEditing: (attribute: SDK.DOMModel.Attribute, result: string) => void;
34
+ onCancelEditing: (attribute: SDK.DOMModel.Attribute) => void;
35
+ attributeBeingEdited: SDK.DOMModel.Attribute|null;
36
+ attributes: SDK.DOMModel.Attribute[];
37
+ }
38
+
39
+ type View = (input: ViewInput, output: object, target: HTMLElement) => void;
40
+ export const DEFAULT_VIEW: View = (input, output, target) => {
41
+ const MAX_CONTENT_LENGTH = 10000;
42
+
43
+ const onStartEditing = (attribute: SDK.DOMModel.Attribute, e: MouseEvent): void => {
44
+ e.consume(true);
45
+ input.onStartEditing(attribute);
46
+ };
47
+
48
+ const propertyCompletions = (attribute: SDK.DOMModel.Attribute): Lit.LitTemplate => {
49
+ const values = input.propertyCompletions.get(attribute);
50
+ if (!values?.length) {
51
+ return Lit.nothing;
52
+ }
53
+
54
+ return html`<datalist id=completions>
55
+ ${values.map(value => html`<option>${value}</option>`)}
56
+ </datalist>`;
57
+ };
58
+
59
+ render(
60
+ // clang-format off
61
+ input.attributes.length === 0 ?
62
+ html`
63
+ <style>${accessibilityPropertiesStyles}</style>
64
+ <devtools-widget
65
+ .widgetConfig=${UI.Widget.widgetConfig(UI.EmptyWidget.EmptyWidget,
66
+ {text: i18nString(UIStrings.noAriaAttributes)})}
67
+ class="gray-info-message info-message-overflow"></devtools-widget>` :
68
+ html`<devtools-tree
69
+ hide-overflow
70
+ .template=${html`
71
+ <ul role="tree">
72
+ ${input.attributes?.map(attribute => html`
73
+ <li role="treeitem">
74
+ <style>${accessibilityPropertiesStyles}</style>
75
+ <span class="ax-name monospace" @mousedown=${onStartEditing.bind(null, attribute)}>
76
+ ${attribute.name}
77
+ </span>
78
+ <span class="separator" @mousedown=${onStartEditing.bind(null, attribute)}>${':\xA0'}</span>
79
+ <devtools-prompt
80
+ completions=completions
81
+ class="monospace"
82
+ @mousedown=${onStartEditing.bind(null, attribute)}
83
+ .completionTimeout=${0}
84
+ ?editing=${input.attributeBeingEdited === attribute}
85
+ @commit=${(e: UI.TextPrompt.TextPromptElement.CommitEvent) =>
86
+ input.onCommitEditing(attribute, e.detail)}
87
+ @cancel=${() => input.onCancelEditing(attribute)}>
88
+ ${Platform.StringUtilities.trimMiddle(attribute.value, MAX_CONTENT_LENGTH)}
89
+ ${propertyCompletions(attribute)}
90
+ </devtools-prompt>
91
+ </li>`)}
92
+ </ul>
93
+ `}></devtools-tree>`,
94
+ // clang-format on
95
+ target);
96
+ };
97
+
27
98
  export class ARIAAttributesPane extends AccessibilitySubPane {
28
- private readonly noPropertiesInfo: Element;
29
- private readonly treeOutline: UI.TreeOutline.TreeOutline;
99
+ readonly #view: View;
100
+ #attributeBeingEdited: SDK.DOMModel.Attribute|null = null;
30
101
 
31
- constructor() {
102
+ constructor(view = DEFAULT_VIEW) {
32
103
  super({
33
104
  title: i18nString(UIStrings.ariaAttributes),
34
105
  viewId: 'aria-attributes',
35
106
  jslog: `${VisualLogging.section('aria-attributes')}`,
36
107
  });
37
108
 
38
- this.noPropertiesInfo = this.createInfo(i18nString(UIStrings.noAriaAttributes));
39
- this.treeOutline = this.createTreeOutline();
109
+ this.#view = view;
40
110
  }
41
111
 
42
112
  override setNode(node: SDK.DOMModel.DOMNode|null): void {
43
113
  super.setNode(node);
44
- this.treeOutline.removeChildren();
45
- if (!node) {
46
- return;
47
- }
48
- const target = node.domModel().target();
49
- const attributes = node.attributes();
50
- for (let i = 0; i < attributes.length; ++i) {
51
- const attribute = attributes[i];
52
- if (!this.isARIAAttribute(attribute)) {
53
- continue;
54
- }
114
+ this.requestUpdate();
115
+ }
55
116
 
56
- this.treeOutline.appendChild(new ARIAAttributesTreeElement(this, attribute, target));
57
- }
117
+ override performUpdate(): void {
118
+ const onStartEditing = (attribute: SDK.DOMModel.Attribute): void => {
119
+ this.#attributeBeingEdited = attribute;
120
+ this.requestUpdate();
121
+ };
122
+ const onCancelEditing = (attribute: SDK.DOMModel.Attribute): void => {
123
+ if (attribute === this.#attributeBeingEdited) {
124
+ this.#attributeBeingEdited = null;
125
+ }
126
+ this.requestUpdate();
127
+ };
128
+
129
+ const onCommitEditing = (attribute: SDK.DOMModel.Attribute, result: string): void => {
130
+ // Make the changes to the attribute
131
+ const node = this.node();
132
+ if (node && attribute.value !== result) {
133
+ node.setAttributeValue(attribute.name, result);
134
+ }
135
+ if (attribute === this.#attributeBeingEdited) {
136
+ this.#attributeBeingEdited = null;
137
+ }
138
+ this.requestUpdate();
139
+ };
58
140
 
59
- const foundAttributes = (this.treeOutline.rootElement().childCount() !== 0);
60
- this.noPropertiesInfo.classList.toggle('hidden', foundAttributes);
61
- this.treeOutline.element.classList.toggle('hidden', !foundAttributes);
62
- }
141
+ const attributes = this.node()?.attributes()?.filter(attribute => this.isARIAAttribute(attribute)) ?? [];
142
+ const propertyCompletions =
143
+ new Map(attributes.map(attribute => [attribute, ariaMetadata().valuesForProperty(attribute.name)]));
63
144
 
64
- getTreeOutlineForTesting(): Readonly<UI.TreeOutline.TreeOutline>|undefined {
65
- return this.treeOutline;
145
+ const input: ViewInput = {
146
+ attributeBeingEdited: this.#attributeBeingEdited,
147
+ attributes,
148
+ onStartEditing,
149
+ onCommitEditing,
150
+ onCancelEditing,
151
+ propertyCompletions,
152
+ };
153
+ this.#view(input, {}, this.contentElement);
66
154
  }
67
155
 
68
156
  private isARIAAttribute(attribute: SDK.DOMModel.Attribute): boolean {
69
157
  return SDK.DOMModel.ARIA_ATTRIBUTES.has(attribute.name);
70
158
  }
71
159
  }
72
-
73
- export class ARIAAttributesTreeElement extends UI.TreeOutline.TreeElement {
74
- private readonly parentPane: ARIAAttributesPane;
75
- private readonly attribute: SDK.DOMModel.Attribute;
76
- private nameElement?: HTMLSpanElement;
77
- private valueElement?: Element;
78
- private prompt?: ARIAAttributePrompt;
79
-
80
- constructor(parentPane: ARIAAttributesPane, attribute: SDK.DOMModel.Attribute, _target: SDK.Target.Target) {
81
- super('');
82
-
83
- this.parentPane = parentPane;
84
- this.attribute = attribute;
85
-
86
- this.selectable = false;
87
- }
88
-
89
- static createARIAValueElement(value: string): Element {
90
- const valueElement = document.createElement('span');
91
- valueElement.classList.add('monospace');
92
- // TODO(aboxhall): quotation marks?
93
- valueElement.setTextContentTruncatedIfNeeded(value || '');
94
- return valueElement;
95
- }
96
-
97
- override onattach(): void {
98
- this.populateListItem();
99
- this.listItemElement.addEventListener('click', this.mouseClick.bind(this));
100
- }
101
-
102
- getPromptForTesting(): Readonly<ARIAAttributePrompt>|undefined {
103
- return this.prompt;
104
- }
105
-
106
- private populateListItem(): void {
107
- this.listItemElement.removeChildren();
108
- this.appendNameElement(this.attribute.name);
109
- this.listItemElement.createChild('span', 'separator').textContent = ':\xA0';
110
- this.appendAttributeValueElement(this.attribute.value);
111
- }
112
-
113
- appendNameElement(name: string): void {
114
- this.nameElement = document.createElement('span');
115
- this.nameElement.textContent = name;
116
- this.nameElement.classList.add('ax-name');
117
- this.nameElement.classList.add('monospace');
118
- this.listItemElement.appendChild(this.nameElement);
119
- }
120
-
121
- appendAttributeValueElement(value: string): void {
122
- this.valueElement = ARIAAttributesTreeElement.createARIAValueElement(value);
123
- this.listItemElement.appendChild(this.valueElement);
124
- }
125
-
126
- private mouseClick(event: Event): void {
127
- if (event.target === this.listItemElement) {
128
- return;
129
- }
130
-
131
- event.consume(true);
132
-
133
- this.startEditing();
134
- }
135
-
136
- private startEditing(): void {
137
- const valueElement = this.valueElement;
138
-
139
- if (!valueElement || UI.UIUtils.isBeingEdited(valueElement)) {
140
- return;
141
- }
142
-
143
- const previousContent = valueElement.textContent || '';
144
-
145
- function blurListener(this: ARIAAttributesTreeElement, previousContent: string, event: Event): void {
146
- const target = event.target as HTMLElement;
147
- const text = target.textContent || '';
148
- this.editingCommitted(text, previousContent);
149
- }
150
-
151
- const attributeName = (this.nameElement as HTMLSpanElement).textContent || '';
152
- this.prompt = new ARIAAttributePrompt(ariaMetadata().valuesForProperty(attributeName));
153
- this.prompt.setAutocompletionTimeout(0);
154
- const proxyElement =
155
- this.prompt.attachAndStartEditing(valueElement, blurListener.bind(this, previousContent)) as HTMLElement;
156
-
157
- proxyElement.addEventListener('keydown', event => this.editingValueKeyDown(previousContent, event), false);
158
-
159
- const selection = valueElement.getComponentSelection();
160
- if (selection) {
161
- selection.selectAllChildren(valueElement);
162
- }
163
- }
164
-
165
- private removePrompt(): void {
166
- if (!this.prompt) {
167
- return;
168
- }
169
- this.prompt.detach();
170
- delete this.prompt;
171
- }
172
-
173
- private editingCommitted(userInput: string, previousContent: string): void {
174
- this.removePrompt();
175
-
176
- // Make the changes to the attribute
177
- if (userInput !== previousContent) {
178
- const node = this.parentPane.node() as SDK.DOMModel.DOMNode;
179
- node.setAttributeValue(this.attribute.name, userInput);
180
- }
181
- }
182
-
183
- private editingCancelled(): void {
184
- this.removePrompt();
185
- this.populateListItem();
186
- }
187
-
188
- private editingValueKeyDown(previousContent: string, event: KeyboardEvent): void {
189
- if (event.handled) {
190
- return;
191
- }
192
-
193
- if (event.key === 'Enter') {
194
- const target = event.target as HTMLElement;
195
- this.editingCommitted(target.textContent || '', previousContent);
196
- event.consume();
197
- return;
198
- }
199
-
200
- if (Platform.KeyboardUtilities.isEscKey(event)) {
201
- this.editingCancelled();
202
- event.consume();
203
- return;
204
- }
205
- }
206
- }
207
-
208
- export class ARIAAttributePrompt extends UI.TextPrompt.TextPrompt {
209
- private readonly ariaCompletions: string[];
210
- constructor(ariaCompletions: string[]) {
211
- super();
212
- this.initialize(this.buildPropertyCompletions.bind(this));
213
-
214
- this.ariaCompletions = ariaCompletions;
215
- }
216
-
217
- private async buildPropertyCompletions(expression: string, prefix: string, force?: boolean):
218
- Promise<UI.SuggestBox.Suggestions> {
219
- prefix = prefix.toLowerCase();
220
- if (!prefix && !force && expression) {
221
- return [];
222
- }
223
- return this.ariaCompletions.filter(value => value.startsWith(prefix)).map(c => {
224
- return {
225
- text: c,
226
- title: undefined,
227
- subtitle: undefined,
228
- priority: undefined,
229
- isSecondary: undefined,
230
- subtitleRenderer: undefined,
231
- selectionRange: undefined,
232
- hideGhostText: undefined,
233
- iconElement: undefined,
234
- };
235
- });
236
- }
237
- }
@@ -120,8 +120,8 @@ const str_ = i18n.i18n.registerUIStrings('panels/accessibility/AccessibilityNode
120
120
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
121
121
  export class AXNodeSubPane extends AccessibilitySubPane {
122
122
  override axNode: SDK.AccessibilityModel.AccessibilityNode|null;
123
- private readonly noNodeInfo: Element;
124
- private readonly ignoredInfo: Element;
123
+ private readonly noNodeInfo: UI.Widget.Widget;
124
+ private readonly ignoredInfo: UI.Widget.Widget;
125
125
  private readonly treeOutline: UI.TreeOutline.TreeOutline;
126
126
  private readonly ignoredReasonsTree: UI.TreeOutline.TreeOutline;
127
127
  constructor() {
@@ -137,7 +137,7 @@ export class AXNodeSubPane extends AccessibilitySubPane {
137
137
  this.contentElement.classList.add('ax-subpane');
138
138
 
139
139
  this.noNodeInfo = this.createInfo(i18nString(UIStrings.noAccessibilityNode));
140
- this.ignoredInfo = this.createInfo(i18nString(UIStrings.accessibilityNodeNotExposed), 'ax-ignored-info hidden');
140
+ this.ignoredInfo = this.createInfo(i18nString(UIStrings.accessibilityNodeNotExposed), 'ax-ignored-info', 'hidden');
141
141
 
142
142
  this.treeOutline = this.createTreeOutline();
143
143
  this.ignoredReasonsTree = this.createTreeOutline();
@@ -160,21 +160,21 @@ export class AXNodeSubPane extends AccessibilitySubPane {
160
160
 
161
161
  if (!axNode) {
162
162
  treeOutline.element.classList.add('hidden');
163
- this.ignoredInfo.classList.add('hidden');
163
+ this.ignoredInfo.element.classList.add('hidden');
164
164
  ignoredReasons.element.classList.add('hidden');
165
165
 
166
- this.noNodeInfo.classList.remove('hidden');
166
+ this.noNodeInfo.element.classList.remove('hidden');
167
167
  this.element.classList.add('ax-ignored-node-pane');
168
168
 
169
169
  return;
170
170
  }
171
171
 
172
172
  if (axNode.ignored()) {
173
- this.noNodeInfo.classList.add('hidden');
173
+ this.noNodeInfo.element.classList.add('hidden');
174
174
  treeOutline.element.classList.add('hidden');
175
175
  this.element.classList.add('ax-ignored-node-pane');
176
176
 
177
- this.ignoredInfo.classList.remove('hidden');
177
+ this.ignoredInfo.element.classList.remove('hidden');
178
178
  ignoredReasons.element.classList.remove('hidden');
179
179
  function addIgnoredReason(property: Protocol.Accessibility.AXProperty): void {
180
180
  ignoredReasons.appendChild(
@@ -191,9 +191,9 @@ export class AXNodeSubPane extends AccessibilitySubPane {
191
191
  }
192
192
  this.element.classList.remove('ax-ignored-node-pane');
193
193
 
194
- this.ignoredInfo.classList.add('hidden');
194
+ this.ignoredInfo.element.classList.add('hidden');
195
195
  ignoredReasons.element.classList.add('hidden');
196
- this.noNodeInfo.classList.add('hidden');
196
+ this.noNodeInfo.element.classList.add('hidden');
197
197
 
198
198
  treeOutline.element.classList.remove('hidden');
199
199
 
@@ -33,10 +33,12 @@ export class AccessibilitySubPane extends UI.View.SimpleView {
33
33
  this.nodeInternal = node;
34
34
  }
35
35
 
36
- createInfo(textContent: string, className?: string): Element {
37
- const info = this.element.createChild('div', className || 'gray-info-message');
38
- info.classList.add('info-message-overflow');
39
- info.textContent = textContent;
36
+ createInfo(textContent: string, ...classNames: string[]): UI.Widget.Widget {
37
+ const info = new UI.EmptyWidget.EmptyWidget(textContent);
38
+ if (classNames.length === 0) {
39
+ classNames.push('gray-info-message');
40
+ }
41
+ info.element.classList.add(...classNames, 'info-message-overflow');
40
42
  return info;
41
43
  }
42
44
 
@@ -4,6 +4,7 @@
4
4
  * found in the LICENSE file.
5
5
  */
6
6
 
7
+ @scope to (devtools-widget > *) {
7
8
  .ax-name {
8
9
  color: var(--sys-color-token-attribute);
9
10
  flex-shrink: 0;
@@ -38,3 +39,4 @@ span.ax-internal-role {
38
39
  overflow-x: hidden;
39
40
  white-space: normal;
40
41
  }
42
+ }
@@ -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 trace = new SDK.TraceObject.RevealableEvent(event);
1058
- return Common.Revealer.reveal(trace);
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: MarkdownRendererWithCodeBlock, {animate, ref: refFn}: {
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: MarkdownRendererWithCodeBlock,
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: MarkdownRendererWithCodeBlock,
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: MarkdownRendererWithCodeBlock,
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: MarkdownRendererWithCodeBlock,
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: MarkdownRendererWithCodeBlock,
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
+ }