chrome-devtools-frontend 1.0.1548980 → 1.0.1549484

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 (188) hide show
  1. package/docs/contributing/settings-experiments-features.md +35 -0
  2. package/docs/styleguide/ux/patterns.md +27 -0
  3. package/eslint.config.mjs +1 -0
  4. package/front_end/Tests.js +2 -0
  5. package/front_end/core/host/InspectorFrontendHost.ts +26 -558
  6. package/front_end/core/host/InspectorFrontendHostAPI.ts +6 -3
  7. package/front_end/core/host/InspectorFrontendHostStub.ts +558 -0
  8. package/front_end/core/host/ResourceLoader.ts +9 -23
  9. package/front_end/core/host/UserMetrics.ts +4 -4
  10. package/front_end/core/root/DevToolsContext.ts +4 -0
  11. package/front_end/core/root/Runtime.ts +10 -0
  12. package/front_end/core/sdk/CSSMatchedStyles.ts +2 -2
  13. package/front_end/core/sdk/CSSModel.ts +24 -24
  14. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +11 -11
  15. package/front_end/core/sdk/CSSQuery.ts +1 -1
  16. package/front_end/core/sdk/CSSRule.ts +2 -2
  17. package/front_end/core/sdk/CSSStyleDeclaration.ts +1 -1
  18. package/front_end/core/sdk/CSSStyleSheetHeader.ts +1 -1
  19. package/front_end/core/sdk/DOMModel.ts +3 -0
  20. package/front_end/core/sdk/NetworkManager.ts +29 -31
  21. package/front_end/core/sdk/NetworkRequest.ts +4 -0
  22. package/front_end/core/sdk/OverlayModel.ts +2 -2
  23. package/front_end/core/sdk/PageResourceLoader.ts +63 -37
  24. package/front_end/core/sdk/SourceMap.ts +6 -0
  25. package/front_end/core/sdk/SourceMapCache.ts +21 -0
  26. package/front_end/core/sdk/SourceMapManager.ts +7 -6
  27. package/front_end/core/sdk/SourceMapScopesInfo.ts +6 -2
  28. package/front_end/core/sdk/TargetManager.ts +14 -2
  29. package/front_end/core/sdk/sdk-meta.ts +13 -0
  30. package/front_end/entrypoints/formatter_worker/FormatterActions.ts +1 -0
  31. package/front_end/entrypoints/formatter_worker/ScopeParser.ts +1 -1
  32. package/front_end/entrypoints/main/MainImpl.ts +13 -3
  33. package/front_end/foundation/Universe.ts +1 -1
  34. package/front_end/generated/Deprecation.ts +18 -4
  35. package/front_end/generated/InspectorBackendCommands.ts +33 -31
  36. package/front_end/generated/SupportedCSSProperties.js +41 -41
  37. package/front_end/generated/protocol-mapping.d.ts +12 -0
  38. package/front_end/generated/protocol-proxy-api.d.ts +11 -0
  39. package/front_end/generated/protocol.ts +70 -35
  40. package/front_end/models/ai_assistance/AiConversation.ts +5 -4
  41. package/front_end/models/ai_assistance/ChangeManager.ts +4 -4
  42. package/front_end/models/ai_assistance/ConversationHandler.ts +0 -15
  43. package/front_end/models/ai_assistance/agents/AiAgent.ts +9 -6
  44. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +135 -3
  45. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +24 -0
  46. package/front_end/models/bindings/CompilerScriptMapping.ts +43 -0
  47. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +19 -0
  48. package/front_end/models/bindings/ResourceMapping.ts +73 -0
  49. package/front_end/models/bindings/ResourceScriptMapping.ts +50 -0
  50. package/front_end/models/issues_manager/GenericIssue.ts +17 -0
  51. package/front_end/models/issues_manager/descriptions/genericNavigationEntryMarkedSkippable.md +7 -0
  52. package/front_end/models/javascript_metadata/NativeFunctions.js +7 -3
  53. package/front_end/models/source_map_scopes/FunctionCodeResolver.snapshot.txt +98 -0
  54. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +270 -0
  55. package/front_end/models/source_map_scopes/source_map_scopes.ts +2 -0
  56. package/front_end/models/workspace/UISourceCode.ts +51 -44
  57. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +76 -34
  58. package/front_end/panels/ai_assistance/aiAssistancePanel.css +1 -0
  59. package/front_end/panels/ai_assistance/components/ChatView.ts +23 -11
  60. package/front_end/panels/application/AppManifestView.ts +3 -2
  61. package/front_end/panels/application/FrameDetailsView.ts +5 -6
  62. package/front_end/panels/application/ServiceWorkersView.ts +2 -2
  63. package/front_end/panels/application/TrustTokensTreeElement.ts +2 -6
  64. package/front_end/panels/application/components/PermissionsPolicySection.ts +201 -157
  65. package/front_end/panels/application/components/ProtocolHandlersView.ts +117 -80
  66. package/front_end/panels/application/components/ServiceWorkerRouterView.ts +47 -41
  67. package/front_end/panels/application/components/StorageMetadataView.ts +31 -34
  68. package/front_end/panels/application/components/TrustTokensView.ts +76 -68
  69. package/front_end/panels/console/ConsoleView.ts +3 -2
  70. package/front_end/panels/console/ConsoleViewMessage.ts +6 -4
  71. package/front_end/panels/console/console-meta.ts +0 -13
  72. package/front_end/panels/developer_resources/DeveloperResourcesView.ts +3 -1
  73. package/front_end/panels/elements/CSSRuleValidator.ts +7 -7
  74. package/front_end/panels/elements/CSSRuleValidatorHelper.ts +2 -2
  75. package/front_end/panels/elements/ElementsTreeElement.ts +16 -13
  76. package/front_end/panels/elements/ElementsTreeOutline.ts +2 -1
  77. package/front_end/panels/elements/LayoutPane.ts +12 -10
  78. package/front_end/panels/elements/StylePropertyTreeElement.ts +12 -12
  79. package/front_end/panels/elements/components/AdornerManager.ts +3 -3
  80. package/front_end/panels/elements/components/StylePropertyEditor.ts +6 -6
  81. package/front_end/panels/linear_memory_inspector/components/LinearMemoryHighlightChipList.ts +27 -49
  82. package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +15 -11
  83. package/front_end/panels/media/PlayerListView.ts +100 -73
  84. package/front_end/panels/media/playerListView.css +5 -0
  85. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +3 -3
  86. package/front_end/panels/network/RequestConditionsDrawer.ts +5 -5
  87. package/front_end/panels/network/components/DirectSocketConnectionView.ts +17 -0
  88. package/front_end/panels/network/resourceChunkView.css +4 -0
  89. package/front_end/panels/security/CookieControlsView.ts +1 -1
  90. package/front_end/panels/sensors/LocationsSettingsTab.ts +1 -1
  91. package/front_end/panels/settings/FrameworkIgnoreListSettingsTab.ts +1 -1
  92. package/front_end/panels/settings/KeybindsSettingsTab.ts +1 -1
  93. package/front_end/panels/settings/SettingsScreen.ts +6 -6
  94. package/front_end/panels/settings/WorkspaceSettingsTab.ts +1 -1
  95. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +1 -1
  96. package/front_end/panels/snippets/SnippetsQuickOpen.ts +4 -2
  97. package/front_end/panels/sources/CSSPlugin.ts +1 -1
  98. package/front_end/panels/sources/FilteredUISourceCodeListProvider.ts +13 -5
  99. package/front_end/panels/sources/GoToLineQuickOpen.ts +4 -2
  100. package/front_end/panels/sources/NavigatorView.ts +2 -2
  101. package/front_end/panels/sources/OpenFileQuickOpen.ts +7 -8
  102. package/front_end/panels/sources/OutlineQuickOpen.ts +6 -3
  103. package/front_end/panels/sources/ProfilePlugin.ts +21 -12
  104. package/front_end/panels/sources/UISourceCodeFrame.ts +0 -1
  105. package/front_end/panels/sources/filteredUISourceCodeListProvider.css +41 -0
  106. package/front_end/panels/timeline/TimelinePanel.ts +17 -18
  107. package/front_end/panels/timeline/TimelineSelectorStatsView.ts +3 -3
  108. package/front_end/panels/timeline/components/insights/SlowCSSSelector.ts +2 -2
  109. package/front_end/panels/timeline/docs/flame_chart_migration.md +11 -16
  110. package/front_end/panels/utils/utils.ts +17 -3
  111. package/front_end/panels/whats_new/ReleaseNoteText.ts +10 -20
  112. package/front_end/panels/whats_new/resources/WNDT.md +8 -8
  113. package/front_end/third_party/chromium/README.chromium +1 -1
  114. package/front_end/third_party/puppeteer/third_party/mitt/README.chromium +1 -0
  115. package/front_end/third_party/puppeteer/third_party/parsel/README.chromium +1 -0
  116. package/front_end/third_party/puppeteer/third_party/rxjs/README.chromium +1 -0
  117. package/front_end/ui/components/adorners/Adorner.ts +1 -1
  118. package/front_end/ui/components/annotations/AnnotationRepository.ts +98 -0
  119. package/front_end/ui/components/annotations/AnnotationType.ts +10 -0
  120. package/front_end/ui/components/annotations/annotations.ts +6 -0
  121. package/front_end/ui/components/buttons/Button.ts +1 -1
  122. package/front_end/ui/components/buttons/FloatingButton.ts +1 -1
  123. package/front_end/ui/components/chrome_link/ChromeLink.ts +1 -1
  124. package/front_end/ui/components/dialogs/ButtonDialog.ts +1 -1
  125. package/front_end/ui/components/dialogs/Dialog.ts +1 -1
  126. package/front_end/ui/components/dialogs/ShortcutDialog.ts +1 -0
  127. package/front_end/ui/components/diff_view/DiffView.ts +1 -1
  128. package/front_end/ui/components/expandable_list/ExpandableList.ts +1 -1
  129. package/front_end/ui/components/highlighting/HighlightElement.ts +1 -0
  130. package/front_end/ui/components/highlighting/MarkupHighlight.ts +162 -0
  131. package/front_end/ui/components/highlighting/highlighting.ts +7 -0
  132. package/front_end/ui/components/icon_button/FileSourceIcon.ts +1 -1
  133. package/front_end/ui/components/icon_button/Icon.ts +4 -2
  134. package/front_end/ui/components/icon_button/IconButton.ts +1 -1
  135. package/front_end/ui/components/issue_counter/IssueCounter.ts +1 -1
  136. package/front_end/ui/components/issue_counter/IssueLinkIcon.ts +1 -1
  137. package/front_end/ui/components/legacy_wrapper/LegacyWrapper.ts +1 -1
  138. package/front_end/ui/components/linkifier/LinkifierImpl.ts +1 -1
  139. package/front_end/ui/components/list/List.ts +184 -0
  140. package/front_end/ui/components/list/list.css +90 -0
  141. package/front_end/ui/components/{cards/cards.ts → list/lists.ts} +3 -3
  142. package/front_end/ui/components/markdown_view/CodeBlock.ts +1 -1
  143. package/front_end/ui/components/markdown_view/MarkdownImage.ts +1 -1
  144. package/front_end/ui/components/markdown_view/MarkdownLink.ts +1 -1
  145. package/front_end/ui/components/markdown_view/MarkdownView.ts +1 -1
  146. package/front_end/ui/components/menus/Menu.ts +1 -1
  147. package/front_end/ui/components/menus/SelectMenu.ts +1 -1
  148. package/front_end/ui/components/node_text/NodeText.ts +1 -1
  149. package/front_end/ui/components/panel_feedback/FeedbackButton.ts +1 -1
  150. package/front_end/ui/components/panel_feedback/PanelFeedback.ts +1 -1
  151. package/front_end/ui/components/panel_feedback/PreviewToggle.ts +1 -1
  152. package/front_end/ui/components/panel_introduction_steps/PanelIntroductionSteps.ts +1 -1
  153. package/front_end/ui/components/report_view/ReportView.ts +1 -1
  154. package/front_end/ui/components/request_link_icon/RequestLinkIcon.ts +1 -1
  155. package/front_end/ui/components/settings/SettingCheckbox.ts +1 -1
  156. package/front_end/ui/components/settings/SettingDeprecationWarning.ts +1 -1
  157. package/front_end/ui/components/snackbars/Snackbar.ts +1 -1
  158. package/front_end/ui/components/spinners/Spinner.ts +1 -1
  159. package/front_end/ui/components/srgb_overlay/SrgbOverlay.ts +1 -1
  160. package/front_end/ui/components/suggestion_input/SuggestionInput.ts +1 -0
  161. package/front_end/ui/components/survey_link/SurveyLink.ts +1 -1
  162. package/front_end/ui/components/switch/SwitchImpl.ts +1 -1
  163. package/front_end/ui/components/text_editor/TextEditor.ts +1 -0
  164. package/front_end/ui/components/text_prompt/TextPrompt.ts +1 -1
  165. package/front_end/ui/components/tooltips/Tooltip.ts +1 -1
  166. package/front_end/ui/components/tree_outline/TreeOutline.ts +1 -1
  167. package/front_end/ui/kit/kit.ts +5 -0
  168. package/front_end/ui/legacy/TabbedPane.ts +98 -0
  169. package/front_end/ui/legacy/UIUtils.ts +0 -184
  170. package/front_end/ui/legacy/ViewManager.ts +23 -8
  171. package/front_end/ui/legacy/ViewRegistration.ts +21 -22
  172. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +5 -4
  173. package/front_end/ui/legacy/components/perf_ui/LineLevelProfile.ts +73 -35
  174. package/front_end/ui/legacy/components/perf_ui/LiveHeapProfile.ts +11 -2
  175. package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +12 -13
  176. package/front_end/ui/legacy/components/quick_open/FilteredListWidget.ts +7 -16
  177. package/front_end/ui/legacy/components/quick_open/HelpQuickOpen.ts +5 -6
  178. package/front_end/ui/legacy/components/quick_open/filteredListWidget.css +18 -65
  179. package/front_end/ui/legacy/components/source_frame/JSONView.ts +2 -1
  180. package/front_end/ui/legacy/tabbedPane.css +10 -0
  181. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  182. package/inspector_overlay/README.md +3 -3
  183. package/mcp/HostBindings.ts +310 -0
  184. package/mcp/mcp.ts +17 -0
  185. package/mcp/tsconfig.json +6 -1
  186. package/package.json +26 -24
  187. /package/front_end/ui/{components → kit}/cards/Card.ts +0 -0
  188. /package/front_end/ui/{components → kit}/cards/card.css +0 -0
@@ -0,0 +1,270 @@
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 type * as Platform from '../../core/platform/platform.js';
6
+ import * as SDK from '../../core/sdk/sdk.js';
7
+ import * as Bindings from '../bindings/bindings.js';
8
+ import * as Formatter from '../formatter/formatter.js';
9
+ import * as TextUtils from '../text_utils/text_utils.js';
10
+ import * as Workspace from '../workspace/workspace.js';
11
+
12
+ /** Represents the source code for a given function, including additional context of surrounding lines. */
13
+ export interface FunctionCode {
14
+ functionBounds: Workspace.UISourceCode.UIFunctionBounds;
15
+ /** The text of `uiSourceCode`. */
16
+ text: TextUtils.Text.Text;
17
+ /** The function text. */
18
+ code: string;
19
+ /** The range of `code` within `text`. */
20
+ range: TextUtils.TextRange.TextRange;
21
+ /** The function text, plus some additional context before and after. The actual function is wrapped in <FUNCTION_START>...<FUNCTION_END> */
22
+ codeWithContext: string;
23
+ /** The range of `codeWithContext` within `text`. */
24
+ rangeWithContext: TextUtils.TextRange.TextRange;
25
+ }
26
+
27
+ export interface CreateFunctionCodeOptions {
28
+ /** Number of characters to include before and after the function. Stacks with `contextLineLength`. */
29
+ contextLength?: number;
30
+ /** Number of lines to include before and after the function. Stacks with `contextLength`. */
31
+ contextLineLength?: number;
32
+ /** If true, appends profile data from the trace at the end of every line of the function in `codeWithContext`. This should match what is seen in the formatted view in the Sources panel. */
33
+ appendProfileData?: boolean;
34
+ }
35
+
36
+ interface InputData {
37
+ text: TextUtils.Text.Text;
38
+ formattedContent: Formatter.ScriptFormatter.FormattedContent|null;
39
+ performanceData: Workspace.UISourceCode.LineColumnProfileMap|undefined;
40
+ }
41
+
42
+ const inputCache = new WeakMap<Workspace.UISourceCode.UISourceCode, Promise<InputData>>();
43
+
44
+ async function prepareInput(uiSourceCode: Workspace.UISourceCode.UISourceCode, content: string): Promise<InputData> {
45
+ const formattedContent = await format(uiSourceCode, content);
46
+ const text = new TextUtils.Text.Text(formattedContent ? formattedContent.formattedContent : content);
47
+ let performanceData = uiSourceCode.getDecorationData(Workspace.UISourceCode.DecoratorType.PERFORMANCE) as
48
+ Workspace.UISourceCode.LineColumnProfileMap |
49
+ undefined;
50
+
51
+ // Map profile data to the formatted view of the text.
52
+ if (formattedContent && performanceData) {
53
+ performanceData = Workspace.UISourceCode.createMappedProfileData(performanceData, (line, column) => {
54
+ return formattedContent.formattedMapping.originalToFormatted(line, column);
55
+ });
56
+ }
57
+
58
+ return {text, formattedContent, performanceData};
59
+ }
60
+
61
+ /** Formatting and parsing line endings for Text is expensive, so cache it. */
62
+ async function prepareInputAndCache(
63
+ uiSourceCode: Workspace.UISourceCode.UISourceCode, content: string): Promise<InputData> {
64
+ let cachedPromise = inputCache.get(uiSourceCode);
65
+ if (cachedPromise) {
66
+ return await cachedPromise;
67
+ }
68
+
69
+ cachedPromise = prepareInput(uiSourceCode, content);
70
+ inputCache.set(uiSourceCode, cachedPromise);
71
+ return await cachedPromise;
72
+ }
73
+
74
+ function extractPerformanceDataByLine(
75
+ textRange: TextUtils.TextRange.TextRange, performanceData: Workspace.UISourceCode.LineColumnProfileMap): number[] {
76
+ const {startLine, startColumn, endLine, endColumn} = textRange;
77
+ const byLine = new Array(endLine - startLine + 1).fill(0);
78
+
79
+ for (let line = startLine; line <= endLine; line++) {
80
+ const lineData = performanceData.get(line + 1);
81
+ if (!lineData) {
82
+ continue;
83
+ }
84
+
85
+ // Fast-path for when the entire line's data is relevant.
86
+ if (line !== startLine && line !== endLine) {
87
+ byLine[line - startLine] = lineData.values().reduce((acc, cur) => acc + cur);
88
+ continue;
89
+ }
90
+
91
+ const column0 = line === startLine ? startColumn + 1 : 0;
92
+ const column1 = line === endLine ? endColumn + 1 : Number.POSITIVE_INFINITY;
93
+
94
+ let totalData = 0;
95
+ for (const [column, data] of lineData) {
96
+ if (column >= column0 && column <= column1) {
97
+ totalData += data;
98
+ }
99
+ }
100
+
101
+ byLine[line - startLine] = totalData;
102
+ }
103
+
104
+ return byLine.map(data => Math.round(data * 10) / 10);
105
+ }
106
+
107
+ function createFunctionCode(
108
+ inputData: InputData, functionBounds: Workspace.UISourceCode.UIFunctionBounds,
109
+ options?: CreateFunctionCodeOptions): FunctionCode {
110
+ let {startLine, startColumn, endLine, endColumn} = functionBounds.range;
111
+ if (inputData.formattedContent) {
112
+ const startMapped = inputData.formattedContent.formattedMapping.originalToFormatted(startLine, startColumn);
113
+ startLine = startMapped[0];
114
+ startColumn = startMapped[1];
115
+
116
+ const endMapped = inputData.formattedContent.formattedMapping.originalToFormatted(endLine, endColumn);
117
+ endLine = endMapped[0];
118
+ endColumn = endMapped[1];
119
+ }
120
+
121
+ const text = inputData.text;
122
+ const content = text.value();
123
+
124
+ // Define two ranges - the first is just the function bounds, the second includes
125
+ // that plus some surrounding context as dictated by the options.
126
+ const range = new TextUtils.TextRange.TextRange(startLine, startColumn, endLine, endColumn);
127
+
128
+ const functionStartOffset = text.offsetFromPosition(startLine, startColumn);
129
+ const functionEndOffset = text.offsetFromPosition(endLine, endColumn);
130
+
131
+ let contextStartOffset = 0;
132
+ if (options?.contextLength !== undefined) {
133
+ const contextLength = options.contextLength;
134
+ contextStartOffset = Math.max(contextStartOffset, functionStartOffset - contextLength);
135
+ }
136
+ if (options?.contextLineLength !== undefined) {
137
+ const contextLineLength = options.contextLineLength;
138
+ const position = text.offsetFromPosition(Math.max(startLine - contextLineLength, 0), 0);
139
+ contextStartOffset = Math.max(contextStartOffset, position);
140
+ }
141
+
142
+ let contextEndOffset = content.length;
143
+ if (options?.contextLength !== undefined) {
144
+ const contextLength = options.contextLength;
145
+ contextEndOffset = Math.min(contextEndOffset, functionEndOffset + contextLength);
146
+ }
147
+ if (options?.contextLineLength !== undefined) {
148
+ const contextLineLength = options.contextLineLength;
149
+ const position =
150
+ text.offsetFromPosition(Math.min(endLine + contextLineLength, text.lineCount() - 1), Number.POSITIVE_INFINITY);
151
+ contextEndOffset = Math.min(contextEndOffset, position);
152
+ }
153
+
154
+ const contextStart = text.positionFromOffset(contextStartOffset);
155
+ const contextEnd = text.positionFromOffset(contextEndOffset);
156
+ const rangeWithContext = new TextUtils.TextRange.TextRange(
157
+ contextStart.lineNumber, contextStart.columnNumber, contextEnd.lineNumber, contextEnd.columnNumber);
158
+
159
+ // Grab substrings for the function range, and for the context range.
160
+ const code = content.substring(functionStartOffset, functionEndOffset);
161
+ const before = content.substring(contextStartOffset, functionStartOffset);
162
+ const after = content.substring(functionEndOffset, contextEndOffset);
163
+
164
+ let codeWithContext;
165
+ if (options?.appendProfileData && inputData.performanceData) {
166
+ const performanceDataByLine = extractPerformanceDataByLine(range, inputData.performanceData);
167
+ const lines = performanceDataByLine.map((data, i) => {
168
+ let line = text.lineAt(startLine + i);
169
+
170
+ const isLastLine = i === performanceDataByLine.length - 1;
171
+ if (i === 0) {
172
+ if (isLastLine) {
173
+ line = line.substring(startColumn, endColumn);
174
+ } else {
175
+ line = line.substring(startColumn);
176
+ }
177
+ } else if (isLastLine) {
178
+ line = line.substring(0, endColumn);
179
+ }
180
+
181
+ if (isLastLine) {
182
+ // Don't ever annotate the last line - it could make the rest of the code on
183
+ // that line get commented out.
184
+ data = 0;
185
+ }
186
+
187
+ return data ? `${line} // ${data} ms` : line;
188
+ });
189
+ const annotatedCode = lines.join('\n');
190
+ codeWithContext = before + `<FUNCTION_START>${annotatedCode}<FUNCTION_END>` + after;
191
+ } else {
192
+ codeWithContext = before + `<FUNCTION_START>${code}<FUNCTION_END>` + after;
193
+ }
194
+
195
+ return {
196
+ functionBounds,
197
+ text,
198
+ code,
199
+ range,
200
+ codeWithContext,
201
+ rangeWithContext,
202
+ };
203
+ }
204
+
205
+ /**
206
+ * The input location may be a source mapped location or a raw location.
207
+ */
208
+ export async function getFunctionCodeFromLocation(
209
+ target: SDK.Target.Target, url: Platform.DevToolsPath.UrlString, line: number, column: number,
210
+ options?: CreateFunctionCodeOptions): Promise<FunctionCode|null> {
211
+ const debuggerModel = target.model(SDK.DebuggerModel.DebuggerModel);
212
+ if (!debuggerModel) {
213
+ throw new Error('missing debugger model');
214
+ }
215
+
216
+ let uiSourceCode;
217
+ const debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
218
+ const projects = debuggerWorkspaceBinding.workspace.projectsForType(Workspace.Workspace.projectTypes.Network);
219
+ for (const project of projects) {
220
+ uiSourceCode = project.uiSourceCodeForURL(url);
221
+ if (uiSourceCode) {
222
+ break;
223
+ }
224
+ }
225
+
226
+ if (!uiSourceCode) {
227
+ return null;
228
+ }
229
+
230
+ const rawLocations = await debuggerWorkspaceBinding.uiLocationToRawLocations(uiSourceCode, line, column);
231
+ const rawLocation = rawLocations.at(-1);
232
+ if (!rawLocation) {
233
+ return null;
234
+ }
235
+
236
+ return await getFunctionCodeFromRawLocation(rawLocation, options);
237
+ }
238
+
239
+ async function format(uiSourceCode: Workspace.UISourceCode.UISourceCode, content: string):
240
+ Promise<Formatter.ScriptFormatter.FormattedContent|null> {
241
+ const contentType = uiSourceCode.contentType();
242
+ const shouldFormat = !contentType.isFromSourceMap() && (contentType.isDocument() || contentType.isScript()) &&
243
+ TextUtils.TextUtils.isMinified(content);
244
+ if (!shouldFormat) {
245
+ return null;
246
+ }
247
+
248
+ return await Formatter.ScriptFormatter.formatScriptContent(contentType.canonicalMimeType(), content, '\t');
249
+ }
250
+
251
+ /**
252
+ * Returns a {@link FunctionCode} for the given raw location.
253
+ */
254
+ export async function getFunctionCodeFromRawLocation(
255
+ rawLocation: SDK.DebuggerModel.Location, options?: CreateFunctionCodeOptions): Promise<FunctionCode|null> {
256
+ const debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
257
+ const functionBounds = await debuggerWorkspaceBinding.functionBoundsAtRawLocation(rawLocation);
258
+ if (!functionBounds) {
259
+ return null;
260
+ }
261
+
262
+ await functionBounds.uiSourceCode.requestContentData();
263
+ const content = functionBounds.uiSourceCode.content();
264
+ if (!content) {
265
+ return null;
266
+ }
267
+
268
+ const inputData = await prepareInputAndCache(functionBounds.uiSourceCode, content);
269
+ return createFunctionCode(inputData, functionBounds, options);
270
+ }
@@ -2,10 +2,12 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
+ import * as FunctionCodeResolver from './FunctionCodeResolver.js';
5
6
  import * as NamesResolver from './NamesResolver.js';
6
7
  import * as ScopeChainModel from './ScopeChainModel.js';
7
8
 
8
9
  export {
10
+ FunctionCodeResolver,
9
11
  NamesResolver,
10
12
  ScopeChainModel,
11
13
  };
@@ -35,9 +35,7 @@ export class UISourceCode extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
35
35
  #contentType: Common.ResourceType.ResourceType;
36
36
  #requestContentPromise: Promise<TextUtils.ContentData.ContentDataOrError>|null = null;
37
37
  #decorations = new Map<string, any>();
38
- #formattedDecorations = new Map<string, any>();
39
38
  #hasCommits = false;
40
- #prettied = false;
41
39
  #messages: Set<Message>|null = null;
42
40
  #content: TextUtils.ContentData.ContentDataOrError|null = null;
43
41
  #forceLoadOnCheckContent = false;
@@ -499,9 +497,6 @@ export class UISourceCode extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
499
497
  }
500
498
 
501
499
  getDecorationData(type: string): any {
502
- if (this.#prettied && this.#formattedDecorations.get(type)) {
503
- return this.#formattedDecorations.get(type);
504
- }
505
500
  return this.#decorations.get(type);
506
501
  }
507
502
 
@@ -513,45 +508,6 @@ export class UISourceCode extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
513
508
  return this.#disableEdit;
514
509
  }
515
510
 
516
- formatChanged(format: {originalToFormatted(lineNumber: number, columnNumber?: number): number[]}|null): void {
517
- if (this.#prettied === Boolean(format)) {
518
- return;
519
- }
520
- this.#prettied = Boolean(format);
521
- if (!format) {
522
- this.dispatchEventToListeners(Events.DecorationChanged, DecoratorType.PERFORMANCE);
523
- return;
524
- }
525
- const performanceDecorations = this.#decorations.get(DecoratorType.PERFORMANCE);
526
- if (!performanceDecorations) {
527
- return;
528
- }
529
- let formattedPerformanceDecorations: Map<number, Map<number, number>> =
530
- this.#formattedDecorations.get(DecoratorType.PERFORMANCE);
531
- if (!formattedPerformanceDecorations) {
532
- formattedPerformanceDecorations = new Map<number, Map<number, number>>();
533
- this.#formattedDecorations.set(DecoratorType.PERFORMANCE, formattedPerformanceDecorations);
534
- } else {
535
- formattedPerformanceDecorations.clear();
536
- }
537
-
538
- for (const [lineNumber, columnData] of performanceDecorations) {
539
- for (const [columnNumber, data] of columnData) {
540
- const [formattedLineNumber, formattedColumnNumber] =
541
- format.originalToFormatted(lineNumber - 1, columnNumber - 1);
542
- const oneBasedFormattedLineNumber = formattedLineNumber + 1;
543
- const oneBasedFormattedColumnNumber = formattedColumnNumber + 1;
544
- let lineData = formattedPerformanceDecorations.get(oneBasedFormattedLineNumber);
545
- if (!lineData) {
546
- lineData = new Map();
547
- formattedPerformanceDecorations.set(oneBasedFormattedLineNumber, lineData);
548
- }
549
- lineData.set(oneBasedFormattedColumnNumber, (lineData.get(oneBasedFormattedColumnNumber) || 0) + data);
550
- }
551
- }
552
- this.dispatchEventToListeners(Events.DecorationChanged, 'performance');
553
- }
554
-
555
511
  isIgnoreListed(): boolean {
556
512
  return IgnoreListManager.instance().isUserOrSourceMapIgnoreListedUISourceCode(this);
557
513
  }
@@ -677,6 +633,21 @@ export class UILocationRange {
677
633
  }
678
634
  }
679
635
 
636
+ /**
637
+ * A text range inside a specific {@link UISourceCode}, representing a function.
638
+ */
639
+ export class UIFunctionBounds {
640
+ readonly uiSourceCode: UISourceCode;
641
+ readonly range: TextUtils.TextRange.TextRange;
642
+ readonly name: string;
643
+
644
+ constructor(uiSourceCode: UISourceCode, range: TextUtils.TextRange.TextRange, name: string) {
645
+ this.uiSourceCode = uiSourceCode;
646
+ this.range = range;
647
+ this.name = name;
648
+ }
649
+ }
650
+
680
651
  /**
681
652
  * A message associated with a range in a `UISourceCode`. The range will be
682
653
  * underlined starting at the range's start and ending at the line end (the
@@ -746,3 +717,39 @@ export const enum DecoratorType {
746
717
  MEMORY = 'memory',
747
718
  COVERAGE = 'coverage',
748
719
  }
720
+
721
+ /** 1-based. line => column => value */
722
+ export type LineColumnProfileMap = Map<number, Map<number, number>>;
723
+ /** Used by ProfilePlugin to track runtime/memory costs. */
724
+ export type ProfileDataMap = Map<UISourceCode, LineColumnProfileMap>;
725
+
726
+ /**
727
+ * Converts an existing LineColumnProfileMap to a new one using the provided mapping.
728
+ *
729
+ * The input and output line/column of originalToMappedLocation is 0-indexed.
730
+ */
731
+ export function createMappedProfileData(
732
+ profileData: LineColumnProfileMap,
733
+ originalToMappedLocation: (line: number, column: number) => number[] | null): LineColumnProfileMap {
734
+ const mappedProfileData: LineColumnProfileMap = new Map();
735
+ for (const [lineNumber, columnData] of profileData) {
736
+ for (const [columnNumber, data] of columnData) {
737
+ const mappedLocation = originalToMappedLocation(lineNumber - 1, columnNumber - 1);
738
+ if (!mappedLocation) {
739
+ continue;
740
+ }
741
+
742
+ const oneBasedFormattedLineNumber = mappedLocation[0] + 1;
743
+ const oneBasedFormattedColumnNumber = mappedLocation[1] + 1;
744
+ let mappedColumnData = mappedProfileData.get(oneBasedFormattedLineNumber);
745
+ if (!mappedColumnData) {
746
+ mappedColumnData = new Map();
747
+ mappedProfileData.set(oneBasedFormattedLineNumber, mappedColumnData);
748
+ }
749
+ mappedColumnData.set(
750
+ oneBasedFormattedColumnNumber, (mappedColumnData.get(oneBasedFormattedColumnNumber) || 0) + data);
751
+ }
752
+ }
753
+
754
+ return mappedProfileData;
755
+ }
@@ -27,6 +27,7 @@ import * as TimelinePanel from '../timeline/timeline.js';
27
27
 
28
28
  import aiAssistancePanelStyles from './aiAssistancePanel.css.js';
29
29
  import {
30
+ type AnswerPart,
30
31
  type ChatMessage,
31
32
  ChatMessageEntity,
32
33
  ChatView,
@@ -1420,7 +1421,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1420
1421
  try {
1421
1422
  let systemMessage: ModelChatMessage = {
1422
1423
  entity: ChatMessageEntity.MODEL,
1423
- steps: [],
1424
+ parts: [],
1424
1425
  };
1425
1426
  let step: Step = {isLoading: true};
1426
1427
 
@@ -1428,9 +1429,14 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1428
1429
  * Commits the step to props only if necessary.
1429
1430
  */
1430
1431
  function commitStep(): void {
1431
- if (systemMessage.steps.at(-1) !== step) {
1432
- systemMessage.steps.push(step);
1432
+ const lastPart = systemMessage.parts.at(-1);
1433
+ if (lastPart?.type === 'step' && lastPart.step === step) {
1434
+ return;
1433
1435
  }
1436
+ systemMessage.parts.push({
1437
+ type: 'step',
1438
+ step,
1439
+ });
1434
1440
  }
1435
1441
 
1436
1442
  this.#isLoading = true;
@@ -1447,15 +1453,15 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1447
1453
  });
1448
1454
  systemMessage = {
1449
1455
  entity: ChatMessageEntity.MODEL,
1450
- steps: [],
1456
+ parts: [],
1451
1457
  };
1452
1458
  this.#messages.push(systemMessage);
1453
1459
  break;
1454
1460
  }
1455
1461
  case AiAssistanceModel.AiAgent.ResponseType.QUERYING: {
1456
1462
  step = {isLoading: true};
1457
- if (!systemMessage.steps.length) {
1458
- systemMessage.steps.push(step);
1463
+ if (!systemMessage.parts.length) {
1464
+ commitStep();
1459
1465
  }
1460
1466
 
1461
1467
  break;
@@ -1479,7 +1485,16 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1479
1485
  break;
1480
1486
  }
1481
1487
  case AiAssistanceModel.AiAgent.ResponseType.SUGGESTIONS: {
1482
- systemMessage.suggestions = data.suggestions;
1488
+ const lastPart = systemMessage.parts.at(-1);
1489
+ if (lastPart?.type === 'answer') {
1490
+ lastPart.suggestions = data.suggestions;
1491
+ } else {
1492
+ systemMessage.parts.push({
1493
+ type: 'answer',
1494
+ text: '',
1495
+ suggestions: data.suggestions,
1496
+ });
1497
+ }
1483
1498
  break;
1484
1499
  }
1485
1500
  case AiAssistanceModel.AiAgent.ResponseType.SIDE_EFFECT: {
@@ -1504,32 +1519,57 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1504
1519
  break;
1505
1520
  }
1506
1521
  case AiAssistanceModel.AiAgent.ResponseType.ANSWER: {
1507
- systemMessage.suggestions ??= data.suggestions;
1508
- systemMessage.answer = data.text;
1509
1522
  systemMessage.rpcId = data.rpcId;
1523
+ const lastPart = systemMessage.parts.at(-1);
1524
+ if (lastPart?.type === 'answer') {
1525
+ lastPart.text = data.text;
1526
+ if (data.suggestions) {
1527
+ lastPart.suggestions = data.suggestions;
1528
+ }
1529
+ } else {
1530
+ const newPart: AnswerPart = {
1531
+ type: 'answer',
1532
+ text: data.text,
1533
+ };
1534
+ if (data.suggestions) {
1535
+ newPart.suggestions = data.suggestions;
1536
+ }
1537
+ systemMessage.parts.push(newPart);
1538
+ }
1539
+
1510
1540
  // When there is an answer without any thinking steps, we don't want to show the thinking step.
1511
- if (systemMessage.steps.length === 1 && systemMessage.steps[0].isLoading) {
1512
- systemMessage.steps.pop();
1541
+ // TODO(crbug.com/463323934): Remove specially handling this case.
1542
+ if (systemMessage.parts.length > 1) {
1543
+ const firstPart = systemMessage.parts[0];
1544
+ if (firstPart.type === 'step' && firstPart.step.isLoading && !firstPart.step.thought &&
1545
+ !firstPart.step.code && !firstPart.step.contextDetails) {
1546
+ systemMessage.parts.shift();
1547
+ }
1513
1548
  }
1514
1549
  step.isLoading = false;
1515
1550
  break;
1516
1551
  }
1517
1552
  case AiAssistanceModel.AiAgent.ResponseType.ERROR: {
1518
1553
  systemMessage.error = data.error;
1519
- systemMessage.rpcId = undefined;
1520
- const lastStep = systemMessage.steps.at(-1);
1521
- if (lastStep) {
1554
+ const lastPart = systemMessage.parts.at(-1);
1555
+ if (lastPart?.type === 'step') {
1556
+ const lastStep = lastPart.step;
1522
1557
  // Mark the last step as cancelled to make the UI feel better.
1523
1558
  if (data.error === AiAssistanceModel.AiAgent.ErrorType.ABORT) {
1524
1559
  lastStep.canceled = true;
1525
1560
  // If error happens while the step is still loading remove it.
1526
1561
  } else if (lastStep.isLoading) {
1527
- systemMessage.steps.pop();
1562
+ systemMessage.parts.pop();
1528
1563
  }
1529
1564
  }
1565
+
1530
1566
  if (data.error === AiAssistanceModel.AiAgent.ErrorType.BLOCK) {
1531
- systemMessage.answer = undefined;
1567
+ const lastPart = systemMessage.parts.at(-1);
1568
+ if (lastPart?.type === 'answer') {
1569
+ systemMessage.parts.pop();
1570
+ }
1532
1571
  }
1572
+ break;
1533
1573
  }
1534
1574
  }
1535
1575
 
@@ -1578,26 +1618,28 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1578
1618
  export function getResponseMarkdown(message: ModelChatMessage): string {
1579
1619
  const contentParts = ['## AI'];
1580
1620
 
1581
- for (const step of message.steps) {
1582
- if (step.title) {
1583
- contentParts.push(`### ${step.title}`);
1584
- }
1585
- if (step.contextDetails) {
1586
- contentParts.push(AiAssistanceModel.AiConversation.generateContextDetailsMarkdown(step.contextDetails));
1587
- }
1588
- if (step.thought) {
1589
- contentParts.push(step.thought);
1590
- }
1591
- if (step.code) {
1592
- contentParts.push(`**Code executed:**\n\`\`\`\n${step.code.trim()}\n\`\`\``);
1593
- }
1594
- if (step.output) {
1595
- contentParts.push(`**Data returned:**\n\`\`\`\n${step.output}\n\`\`\``);
1621
+ for (const part of message.parts) {
1622
+ if (part.type === 'answer') {
1623
+ contentParts.push(`### Answer\n\n${part.text}`);
1624
+ } else {
1625
+ const step = part.step;
1626
+ if (step.title) {
1627
+ contentParts.push(`### ${step.title}`);
1628
+ }
1629
+ if (step.contextDetails) {
1630
+ contentParts.push(AiAssistanceModel.AiConversation.generateContextDetailsMarkdown(step.contextDetails));
1631
+ }
1632
+ if (step.thought) {
1633
+ contentParts.push(step.thought);
1634
+ }
1635
+ if (step.code) {
1636
+ contentParts.push(`**Code executed:**\n\`\`\`\n${step.code.trim()}\n\`\`\``);
1637
+ }
1638
+ if (step.output) {
1639
+ contentParts.push(`**Data returned:**\n\`\`\`\n${step.output}\n\`\`\``);
1640
+ }
1596
1641
  }
1597
1642
  }
1598
- if (message.answer) {
1599
- contentParts.push(`### Answer\n\n${message.answer}`);
1600
- }
1601
1643
  return contentParts.join('\n\n');
1602
1644
  }
1603
1645
 
@@ -6,6 +6,7 @@
6
6
 
7
7
  .toolbar-container {
8
8
  display: flex;
9
+ flex-wrap: wrap;
9
10
  background-color: var(--sys-color-cdt-base-container);
10
11
  border-bottom: 1px solid var(--sys-color-divider);
11
12
  flex: 0 0 auto;
@@ -204,6 +204,19 @@ export type ImageInputData = {
204
204
  inputType: AiAssistanceModel.AiAgent.MultimodalInputType,
205
205
  };
206
206
 
207
+ export interface AnswerPart {
208
+ type: 'answer';
209
+ text: string;
210
+ suggestions?: [string, ...string[]];
211
+ }
212
+
213
+ export interface StepPart {
214
+ type: 'step';
215
+ step: Step;
216
+ }
217
+
218
+ export type ModelMessagePart = AnswerPart|StepPart;
219
+
207
220
  export interface UserChatMessage {
208
221
  entity: ChatMessageEntity.USER;
209
222
  text: string;
@@ -211,9 +224,7 @@ export interface UserChatMessage {
211
224
  }
212
225
  export interface ModelChatMessage {
213
226
  entity: ChatMessageEntity.MODEL;
214
- steps: Step[];
215
- suggestions?: [string, ...string[]];
216
- answer?: string;
227
+ parts: ModelMessagePart[];
217
228
  error?: AiAssistanceModel.AiAgent.ErrorType;
218
229
  rpcId?: Host.AidaClient.RpcGlobalId;
219
230
  }
@@ -839,20 +850,21 @@ function renderChatMessage({
839
850
  </div>
840
851
  </div>
841
852
  ${Lit.Directives.repeat(
842
- message.steps,
853
+ message.parts,
843
854
  (_, index) => index,
844
- step => {
855
+ (part, index) => {
856
+ const isLastPart = index === message.parts.length - 1;
857
+ if (part.type === 'answer') {
858
+ return html`<p>${renderTextAsMarkdown(part.text, markdownRenderer, { animate: !isReadOnly && isLoading && isLast && isLastPart })}</p>`;
859
+ }
845
860
  return renderStep({
846
- step,
861
+ step: part.step,
847
862
  isLoading,
848
863
  markdownRenderer,
849
- isLast: [...message.steps.values()].at(-1) === step && isLast,
864
+ isLast: isLastPart && isLast,
850
865
  });
851
866
  },
852
867
  )}
853
- ${message.answer
854
- ? html`<p>${renderTextAsMarkdown(message.answer, markdownRenderer, { animate: !isReadOnly && isLoading && isLast })}</p>`
855
- : Lit.nothing}
856
868
  ${renderError(message)}
857
869
  ${isLast && isLoading
858
870
  ? Lit.nothing
@@ -864,7 +876,7 @@ function renderChatMessage({
864
876
  }
865
877
  onFeedbackSubmit(message.rpcId, rating, feedback);
866
878
  },
867
- suggestions: (isLast && !isReadOnly) ? message.suggestions : undefined,
879
+ suggestions: (isLast && !isReadOnly && message.parts.at(-1)?.type === 'answer') ? (message.parts.at(-1) as AnswerPart).suggestions : undefined,
868
880
  onSuggestionClick,
869
881
  onCopyResponseClick: () => onCopyResponseClick(message),
870
882
  canShowFeedbackForm,