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.
- package/docs/contributing/settings-experiments-features.md +35 -0
- package/docs/styleguide/ux/patterns.md +27 -0
- package/eslint.config.mjs +1 -0
- package/front_end/Tests.js +2 -0
- package/front_end/core/host/InspectorFrontendHost.ts +26 -558
- package/front_end/core/host/InspectorFrontendHostAPI.ts +6 -3
- package/front_end/core/host/InspectorFrontendHostStub.ts +558 -0
- package/front_end/core/host/ResourceLoader.ts +9 -23
- package/front_end/core/host/UserMetrics.ts +4 -4
- package/front_end/core/root/DevToolsContext.ts +4 -0
- package/front_end/core/root/Runtime.ts +10 -0
- package/front_end/core/sdk/CSSMatchedStyles.ts +2 -2
- package/front_end/core/sdk/CSSModel.ts +24 -24
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +11 -11
- package/front_end/core/sdk/CSSQuery.ts +1 -1
- package/front_end/core/sdk/CSSRule.ts +2 -2
- package/front_end/core/sdk/CSSStyleDeclaration.ts +1 -1
- package/front_end/core/sdk/CSSStyleSheetHeader.ts +1 -1
- package/front_end/core/sdk/DOMModel.ts +3 -0
- package/front_end/core/sdk/NetworkManager.ts +29 -31
- package/front_end/core/sdk/NetworkRequest.ts +4 -0
- package/front_end/core/sdk/OverlayModel.ts +2 -2
- package/front_end/core/sdk/PageResourceLoader.ts +63 -37
- package/front_end/core/sdk/SourceMap.ts +6 -0
- package/front_end/core/sdk/SourceMapCache.ts +21 -0
- package/front_end/core/sdk/SourceMapManager.ts +7 -6
- package/front_end/core/sdk/SourceMapScopesInfo.ts +6 -2
- package/front_end/core/sdk/TargetManager.ts +14 -2
- package/front_end/core/sdk/sdk-meta.ts +13 -0
- package/front_end/entrypoints/formatter_worker/FormatterActions.ts +1 -0
- package/front_end/entrypoints/formatter_worker/ScopeParser.ts +1 -1
- package/front_end/entrypoints/main/MainImpl.ts +13 -3
- package/front_end/foundation/Universe.ts +1 -1
- package/front_end/generated/Deprecation.ts +18 -4
- package/front_end/generated/InspectorBackendCommands.ts +33 -31
- package/front_end/generated/SupportedCSSProperties.js +41 -41
- package/front_end/generated/protocol-mapping.d.ts +12 -0
- package/front_end/generated/protocol-proxy-api.d.ts +11 -0
- package/front_end/generated/protocol.ts +70 -35
- package/front_end/models/ai_assistance/AiConversation.ts +5 -4
- package/front_end/models/ai_assistance/ChangeManager.ts +4 -4
- package/front_end/models/ai_assistance/ConversationHandler.ts +0 -15
- package/front_end/models/ai_assistance/agents/AiAgent.ts +9 -6
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +135 -3
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +24 -0
- package/front_end/models/bindings/CompilerScriptMapping.ts +43 -0
- package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +19 -0
- package/front_end/models/bindings/ResourceMapping.ts +73 -0
- package/front_end/models/bindings/ResourceScriptMapping.ts +50 -0
- package/front_end/models/issues_manager/GenericIssue.ts +17 -0
- package/front_end/models/issues_manager/descriptions/genericNavigationEntryMarkedSkippable.md +7 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +7 -3
- package/front_end/models/source_map_scopes/FunctionCodeResolver.snapshot.txt +98 -0
- package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +270 -0
- package/front_end/models/source_map_scopes/source_map_scopes.ts +2 -0
- package/front_end/models/workspace/UISourceCode.ts +51 -44
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +76 -34
- package/front_end/panels/ai_assistance/aiAssistancePanel.css +1 -0
- package/front_end/panels/ai_assistance/components/ChatView.ts +23 -11
- package/front_end/panels/application/AppManifestView.ts +3 -2
- package/front_end/panels/application/FrameDetailsView.ts +5 -6
- package/front_end/panels/application/ServiceWorkersView.ts +2 -2
- package/front_end/panels/application/TrustTokensTreeElement.ts +2 -6
- package/front_end/panels/application/components/PermissionsPolicySection.ts +201 -157
- package/front_end/panels/application/components/ProtocolHandlersView.ts +117 -80
- package/front_end/panels/application/components/ServiceWorkerRouterView.ts +47 -41
- package/front_end/panels/application/components/StorageMetadataView.ts +31 -34
- package/front_end/panels/application/components/TrustTokensView.ts +76 -68
- package/front_end/panels/console/ConsoleView.ts +3 -2
- package/front_end/panels/console/ConsoleViewMessage.ts +6 -4
- package/front_end/panels/console/console-meta.ts +0 -13
- package/front_end/panels/developer_resources/DeveloperResourcesView.ts +3 -1
- package/front_end/panels/elements/CSSRuleValidator.ts +7 -7
- package/front_end/panels/elements/CSSRuleValidatorHelper.ts +2 -2
- package/front_end/panels/elements/ElementsTreeElement.ts +16 -13
- package/front_end/panels/elements/ElementsTreeOutline.ts +2 -1
- package/front_end/panels/elements/LayoutPane.ts +12 -10
- package/front_end/panels/elements/StylePropertyTreeElement.ts +12 -12
- package/front_end/panels/elements/components/AdornerManager.ts +3 -3
- package/front_end/panels/elements/components/StylePropertyEditor.ts +6 -6
- package/front_end/panels/linear_memory_inspector/components/LinearMemoryHighlightChipList.ts +27 -49
- package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +15 -11
- package/front_end/panels/media/PlayerListView.ts +100 -73
- package/front_end/panels/media/playerListView.css +5 -0
- package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +3 -3
- package/front_end/panels/network/RequestConditionsDrawer.ts +5 -5
- package/front_end/panels/network/components/DirectSocketConnectionView.ts +17 -0
- package/front_end/panels/network/resourceChunkView.css +4 -0
- package/front_end/panels/security/CookieControlsView.ts +1 -1
- package/front_end/panels/sensors/LocationsSettingsTab.ts +1 -1
- package/front_end/panels/settings/FrameworkIgnoreListSettingsTab.ts +1 -1
- package/front_end/panels/settings/KeybindsSettingsTab.ts +1 -1
- package/front_end/panels/settings/SettingsScreen.ts +6 -6
- package/front_end/panels/settings/WorkspaceSettingsTab.ts +1 -1
- package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +1 -1
- package/front_end/panels/snippets/SnippetsQuickOpen.ts +4 -2
- package/front_end/panels/sources/CSSPlugin.ts +1 -1
- package/front_end/panels/sources/FilteredUISourceCodeListProvider.ts +13 -5
- package/front_end/panels/sources/GoToLineQuickOpen.ts +4 -2
- package/front_end/panels/sources/NavigatorView.ts +2 -2
- package/front_end/panels/sources/OpenFileQuickOpen.ts +7 -8
- package/front_end/panels/sources/OutlineQuickOpen.ts +6 -3
- package/front_end/panels/sources/ProfilePlugin.ts +21 -12
- package/front_end/panels/sources/UISourceCodeFrame.ts +0 -1
- package/front_end/panels/sources/filteredUISourceCodeListProvider.css +41 -0
- package/front_end/panels/timeline/TimelinePanel.ts +17 -18
- package/front_end/panels/timeline/TimelineSelectorStatsView.ts +3 -3
- package/front_end/panels/timeline/components/insights/SlowCSSSelector.ts +2 -2
- package/front_end/panels/timeline/docs/flame_chart_migration.md +11 -16
- package/front_end/panels/utils/utils.ts +17 -3
- package/front_end/panels/whats_new/ReleaseNoteText.ts +10 -20
- package/front_end/panels/whats_new/resources/WNDT.md +8 -8
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/puppeteer/third_party/mitt/README.chromium +1 -0
- package/front_end/third_party/puppeteer/third_party/parsel/README.chromium +1 -0
- package/front_end/third_party/puppeteer/third_party/rxjs/README.chromium +1 -0
- package/front_end/ui/components/adorners/Adorner.ts +1 -1
- package/front_end/ui/components/annotations/AnnotationRepository.ts +98 -0
- package/front_end/ui/components/annotations/AnnotationType.ts +10 -0
- package/front_end/ui/components/annotations/annotations.ts +6 -0
- package/front_end/ui/components/buttons/Button.ts +1 -1
- package/front_end/ui/components/buttons/FloatingButton.ts +1 -1
- package/front_end/ui/components/chrome_link/ChromeLink.ts +1 -1
- package/front_end/ui/components/dialogs/ButtonDialog.ts +1 -1
- package/front_end/ui/components/dialogs/Dialog.ts +1 -1
- package/front_end/ui/components/dialogs/ShortcutDialog.ts +1 -0
- package/front_end/ui/components/diff_view/DiffView.ts +1 -1
- package/front_end/ui/components/expandable_list/ExpandableList.ts +1 -1
- package/front_end/ui/components/highlighting/HighlightElement.ts +1 -0
- package/front_end/ui/components/highlighting/MarkupHighlight.ts +162 -0
- package/front_end/ui/components/highlighting/highlighting.ts +7 -0
- package/front_end/ui/components/icon_button/FileSourceIcon.ts +1 -1
- package/front_end/ui/components/icon_button/Icon.ts +4 -2
- package/front_end/ui/components/icon_button/IconButton.ts +1 -1
- package/front_end/ui/components/issue_counter/IssueCounter.ts +1 -1
- package/front_end/ui/components/issue_counter/IssueLinkIcon.ts +1 -1
- package/front_end/ui/components/legacy_wrapper/LegacyWrapper.ts +1 -1
- package/front_end/ui/components/linkifier/LinkifierImpl.ts +1 -1
- package/front_end/ui/components/list/List.ts +184 -0
- package/front_end/ui/components/list/list.css +90 -0
- package/front_end/ui/components/{cards/cards.ts → list/lists.ts} +3 -3
- package/front_end/ui/components/markdown_view/CodeBlock.ts +1 -1
- package/front_end/ui/components/markdown_view/MarkdownImage.ts +1 -1
- package/front_end/ui/components/markdown_view/MarkdownLink.ts +1 -1
- package/front_end/ui/components/markdown_view/MarkdownView.ts +1 -1
- package/front_end/ui/components/menus/Menu.ts +1 -1
- package/front_end/ui/components/menus/SelectMenu.ts +1 -1
- package/front_end/ui/components/node_text/NodeText.ts +1 -1
- package/front_end/ui/components/panel_feedback/FeedbackButton.ts +1 -1
- package/front_end/ui/components/panel_feedback/PanelFeedback.ts +1 -1
- package/front_end/ui/components/panel_feedback/PreviewToggle.ts +1 -1
- package/front_end/ui/components/panel_introduction_steps/PanelIntroductionSteps.ts +1 -1
- package/front_end/ui/components/report_view/ReportView.ts +1 -1
- package/front_end/ui/components/request_link_icon/RequestLinkIcon.ts +1 -1
- package/front_end/ui/components/settings/SettingCheckbox.ts +1 -1
- package/front_end/ui/components/settings/SettingDeprecationWarning.ts +1 -1
- package/front_end/ui/components/snackbars/Snackbar.ts +1 -1
- package/front_end/ui/components/spinners/Spinner.ts +1 -1
- package/front_end/ui/components/srgb_overlay/SrgbOverlay.ts +1 -1
- package/front_end/ui/components/suggestion_input/SuggestionInput.ts +1 -0
- package/front_end/ui/components/survey_link/SurveyLink.ts +1 -1
- package/front_end/ui/components/switch/SwitchImpl.ts +1 -1
- package/front_end/ui/components/text_editor/TextEditor.ts +1 -0
- package/front_end/ui/components/text_prompt/TextPrompt.ts +1 -1
- package/front_end/ui/components/tooltips/Tooltip.ts +1 -1
- package/front_end/ui/components/tree_outline/TreeOutline.ts +1 -1
- package/front_end/ui/kit/kit.ts +5 -0
- package/front_end/ui/legacy/TabbedPane.ts +98 -0
- package/front_end/ui/legacy/UIUtils.ts +0 -184
- package/front_end/ui/legacy/ViewManager.ts +23 -8
- package/front_end/ui/legacy/ViewRegistration.ts +21 -22
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +5 -4
- package/front_end/ui/legacy/components/perf_ui/LineLevelProfile.ts +73 -35
- package/front_end/ui/legacy/components/perf_ui/LiveHeapProfile.ts +11 -2
- package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +12 -13
- package/front_end/ui/legacy/components/quick_open/FilteredListWidget.ts +7 -16
- package/front_end/ui/legacy/components/quick_open/HelpQuickOpen.ts +5 -6
- package/front_end/ui/legacy/components/quick_open/filteredListWidget.css +18 -65
- package/front_end/ui/legacy/components/source_frame/JSONView.ts +2 -1
- package/front_end/ui/legacy/tabbedPane.css +10 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
- package/inspector_overlay/README.md +3 -3
- package/mcp/HostBindings.ts +310 -0
- package/mcp/mcp.ts +17 -0
- package/mcp/tsconfig.json +6 -1
- package/package.json +26 -24
- /package/front_end/ui/{components → kit}/cards/Card.ts +0 -0
- /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
|
-
|
|
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
|
-
|
|
1432
|
-
|
|
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
|
-
|
|
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.
|
|
1458
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1512
|
-
|
|
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
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
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.
|
|
1562
|
+
systemMessage.parts.pop();
|
|
1528
1563
|
}
|
|
1529
1564
|
}
|
|
1565
|
+
|
|
1530
1566
|
if (data.error === AiAssistanceModel.AiAgent.ErrorType.BLOCK) {
|
|
1531
|
-
|
|
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
|
|
1582
|
-
if (
|
|
1583
|
-
contentParts.push(`### ${
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
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
|
|
|
@@ -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
|
-
|
|
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.
|
|
853
|
+
message.parts,
|
|
843
854
|
(_, index) => index,
|
|
844
|
-
|
|
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:
|
|
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,
|