chrome-devtools-frontend 1.0.1605390 → 1.0.1606789
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/front_end/core/host/UserMetrics.ts +0 -1
- package/front_end/core/platform/api/HostRuntime.ts +9 -6
- package/front_end/core/platform/browser/HostRuntime.ts +2 -2
- package/front_end/core/platform/node/HostRuntime.ts +7 -7
- package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
- package/front_end/core/root/ExperimentNames.ts +0 -1
- package/front_end/core/sdk/CrashReportContextModel.ts +28 -0
- package/front_end/core/sdk/sdk.ts +2 -2
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.ts +3 -3
- package/front_end/entrypoints/heap_snapshot_worker/heap_snapshot_worker-entrypoint.ts +5 -4
- package/front_end/entrypoints/main/MainImpl.ts +0 -101
- package/front_end/models/ai_assistance/ChangeManager.ts +6 -6
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +112 -5
- package/front_end/models/ai_assistance/agents/AiAgent.ts +0 -24
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +2 -2
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +6 -3
- package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +13 -1
- package/front_end/models/ai_assistance/agents/FileAgent.ts +15 -1
- package/front_end/models/ai_assistance/agents/NetworkAgent.ts +15 -1
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +24 -15
- package/front_end/models/ai_assistance/ai_assistance.ts +0 -2
- package/front_end/models/javascript_metadata/NativeFunctions.js +1 -5
- package/front_end/models/web_mcp/WebMCPModel.ts +187 -0
- package/front_end/models/web_mcp/web_mcp.ts +9 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +8 -1
- package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
- package/front_end/panels/ai_assistance/components/AccessibilityAgentMarkdownRenderer.ts +88 -0
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +19 -3
- package/front_end/panels/ai_assistance/components/ChatView.ts +11 -4
- package/front_end/panels/ai_assistance/components/ExportForAgentsDialog.ts +12 -3
- package/front_end/panels/ai_assistance/components/chatMessage.css +10 -0
- package/front_end/panels/ai_assistance/components/chatView.css +0 -2
- package/front_end/panels/ai_assistance/components/exportForAgentsDialog.css +13 -0
- package/front_end/panels/application/WebMCPView.ts +126 -30
- package/front_end/panels/application/webMCPView.css +28 -2
- package/front_end/panels/elements/ElementsTreeElement.ts +1 -1
- package/front_end/panels/elements/StylePropertiesSection.ts +0 -4
- package/front_end/panels/elements/elements.ts +3 -0
- package/front_end/panels/elements/stylePropertiesTreeOutline.css +7 -0
- package/front_end/panels/lighthouse/LighthousePanel.ts +7 -1
- package/front_end/panels/lighthouse/LighthouseProtocolService.ts +25 -2
- package/front_end/panels/profiler/HeapSnapshotProxy.ts +2 -6
- package/front_end/panels/profiler/HeapSnapshotView.ts +7 -16
- package/front_end/panels/profiler/ProfileHeader.ts +1 -4
- package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +27 -22
- package/front_end/panels/timeline/TimelinePanel.ts +0 -153
- package/front_end/panels/timeline/TimelineTreeView.ts +11 -1
- package/front_end/panels/timeline/TimelineUIUtils.ts +2 -1
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/markdown_view/CodeBlock.ts +47 -1
- package/front_end/ui/components/markdown_view/codeBlock.css +8 -0
- package/front_end/ui/legacy/TabbedPane.ts +123 -3
- package/front_end/ui/legacy/Widget.ts +67 -28
- package/front_end/ui/legacy/components/source_frame/JSONView.ts +1 -1
- package/front_end/ui/legacy/components/utils/jsUtils.css +2 -0
- package/front_end/ui/legacy/infobar.css +1 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +0 -1
- package/package.json +1 -1
- package/front_end/core/sdk/WebMCPModel.ts +0 -159
- package/front_end/models/ai_assistance/ConversationHandler.ts +0 -325
|
@@ -8,7 +8,6 @@ import * as Common from '../../core/common/common.js';
|
|
|
8
8
|
import * as Host from '../../core/host/host.js';
|
|
9
9
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
10
10
|
import * as Platform from '../../core/platform/platform.js';
|
|
11
|
-
import * as Root from '../../core/root/root.js';
|
|
12
11
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
13
12
|
import type * as Protocol from '../../generated/protocol.js';
|
|
14
13
|
import * as Bindings from '../../models/bindings/bindings.js';
|
|
@@ -832,7 +831,7 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
|
|
|
832
831
|
|
|
833
832
|
inspectedObjectChanged(
|
|
834
833
|
event: Common.EventTarget.EventTargetEvent<DataGrid.DataGrid.DataGridNode<HeapSnapshotGridNode>>): void {
|
|
835
|
-
const selectedNode =
|
|
834
|
+
const selectedNode = event.data;
|
|
836
835
|
const heapProfilerModel = this.profile.heapProfilerModel();
|
|
837
836
|
if (heapProfilerModel && selectedNode instanceof HeapSnapshotGenericObjectNode) {
|
|
838
837
|
void heapProfilerModel.addInspectedHeapObject(
|
|
@@ -1339,12 +1338,10 @@ export class HeapSnapshotProfileType extends
|
|
|
1339
1338
|
}
|
|
1340
1339
|
|
|
1341
1340
|
override customContent(): Element|null {
|
|
1342
|
-
const showOptionToExposeInternalsInHeapSnapshot = Root.Runtime.experiments.isEnabled(
|
|
1343
|
-
Root.ExperimentNames.ExperimentName.SHOW_OPTION_TO_EXPOSE_INTERNALS_IN_HEAP_SNAPSHOT);
|
|
1344
1341
|
const exposeInternalsInHeapSnapshotCheckbox =
|
|
1345
1342
|
SettingsUI.SettingsUI.createSettingCheckbox(i18nString(UIStrings.exposeInternals), this.exposeInternals);
|
|
1346
1343
|
this.customContentInternal = exposeInternalsInHeapSnapshotCheckbox;
|
|
1347
|
-
return
|
|
1344
|
+
return exposeInternalsInHeapSnapshotCheckbox;
|
|
1348
1345
|
}
|
|
1349
1346
|
|
|
1350
1347
|
override setCustomContentEnabled(enable: boolean): void {
|
|
@@ -1809,9 +1806,7 @@ export class HeapProfileHeader extends ProfileHeader {
|
|
|
1809
1806
|
return;
|
|
1810
1807
|
}
|
|
1811
1808
|
if (this.tempFile) {
|
|
1812
|
-
const error =
|
|
1813
|
-
message: string,
|
|
1814
|
-
} | null);
|
|
1809
|
+
const error = await this.tempFile.copyToOutputStream(fileOutputStream, this.onChunkTransferred.bind(this));
|
|
1815
1810
|
if (error) {
|
|
1816
1811
|
Common.Console.Console.instance().error('Failed to read heap snapshot from temp file: ' + error.message);
|
|
1817
1812
|
}
|
|
@@ -1841,15 +1836,11 @@ export class HeapProfileHeader extends ProfileHeader {
|
|
|
1841
1836
|
this.setupWorker();
|
|
1842
1837
|
const reader = new Bindings.FileUtils.ChunkedFileReader(file, 10000000);
|
|
1843
1838
|
const success = await reader.read((this.receiver as Common.StringOutputStream.OutputStream));
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
} | null);
|
|
1848
|
-
if (error) {
|
|
1849
|
-
this.updateStatus(error.message);
|
|
1850
|
-
}
|
|
1839
|
+
const error = reader.error();
|
|
1840
|
+
if (!success && error) {
|
|
1841
|
+
this.updateStatus(error.message);
|
|
1851
1842
|
}
|
|
1852
|
-
return success ? null :
|
|
1843
|
+
return success ? null : error;
|
|
1853
1844
|
}
|
|
1854
1845
|
|
|
1855
1846
|
override profileType(): HeapSnapshotProfileType {
|
|
@@ -156,10 +156,7 @@ export class ProfileType extends Common.ObjectWrapper.ObjectWrapper<ProfileEvent
|
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
getProfiles(): ProfileHeader[] {
|
|
159
|
-
|
|
160
|
-
return this.#profileBeingRecorded !== profile;
|
|
161
|
-
}
|
|
162
|
-
return this.profiles.filter(isFinished.bind(this));
|
|
159
|
+
return this.profiles.filter(profile => this.#profileBeingRecorded !== profile);
|
|
163
160
|
}
|
|
164
161
|
|
|
165
162
|
customContent(): Element|null {
|
|
@@ -675,28 +675,33 @@ type InfoWidgetView = (input: InfoWidgetViewInput, output: undefined, target: HT
|
|
|
675
675
|
|
|
676
676
|
const INFO_WIDGET_VIEW: InfoWidgetView = (input, _output, target) => {
|
|
677
677
|
// clang-format off
|
|
678
|
-
render(
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
678
|
+
render(html`
|
|
679
|
+
<devtools-tabbed-pane>${input.type === undefined ? html`
|
|
680
|
+
<devtools-widget
|
|
681
|
+
id="request" title=${i18nString(UIStrings.request)}
|
|
682
|
+
?selected=${input.selectedTab === 'request'} disabled
|
|
683
|
+
${widget(UI.EmptyWidget.EmptyWidget, {
|
|
684
|
+
header: i18nString(UIStrings.noMessageSelected),
|
|
685
|
+
text: i18nString(UIStrings.selectAMessageToView)})}>
|
|
686
|
+
</devtools-widget>
|
|
687
|
+
<devtools-widget
|
|
688
|
+
id="response" title=${i18nString(UIStrings.response)}
|
|
689
|
+
?selected=${input.selectedTab === 'response'}
|
|
690
|
+
${widget(UI.EmptyWidget.EmptyWidget, {
|
|
691
|
+
header: i18nString(UIStrings.noMessageSelected),
|
|
692
|
+
text: i18nString(UIStrings.selectAMessageToView)})}>
|
|
693
|
+
</devtools-widget>`: html`
|
|
694
|
+
<devtools-widget
|
|
695
|
+
id="request" title=${i18nString(UIStrings.request)}
|
|
696
|
+
?selected=${input.selectedTab === 'request'} ?disabled=${input.type !== 'sent'}
|
|
697
|
+
${widget(SourceFrame.JSONView.SearchableJsonView, {jsonObject: input.request})}>
|
|
698
|
+
</devtools-widget>
|
|
699
|
+
<devtools-widget
|
|
700
|
+
id="response" title=${i18nString(UIStrings.response)}
|
|
701
|
+
?selected=${input.selectedTab === 'response'}
|
|
702
|
+
${widget(SourceFrame.JSONView.SearchableJsonView, {jsonObject: input.response})}>
|
|
703
|
+
</devtools-widget>`}
|
|
704
|
+
</devtools-tabbed-pane>`, target);
|
|
700
705
|
// clang-format on
|
|
701
706
|
};
|
|
702
707
|
|
|
@@ -54,7 +54,6 @@ import * as TraceBounds from '../../services/trace_bounds/trace_bounds.js';
|
|
|
54
54
|
import * as Tracing from '../../services/tracing/tracing.js';
|
|
55
55
|
import * as Adorners from '../../ui/components/adorners/adorners.js';
|
|
56
56
|
import * as Dialogs from '../../ui/components/dialogs/dialogs.js';
|
|
57
|
-
import * as Snackbars from '../../ui/components/snackbars/snackbars.js';
|
|
58
57
|
import {Link} from '../../ui/kit/kit.js';
|
|
59
58
|
import * as PerfUI from '../../ui/legacy/components/perf_ui/perf_ui.js';
|
|
60
59
|
import * as SettingsUI from '../../ui/legacy/components/settings_ui/settings_ui.js';
|
|
@@ -311,10 +310,6 @@ const UIStrings = {
|
|
|
311
310
|
* @description Title of the shortcuts dialog shown to the user that lists keyboard shortcuts.
|
|
312
311
|
*/
|
|
313
312
|
shortcutsDialogTitle: 'Keyboard shortcuts for flamechart',
|
|
314
|
-
/**
|
|
315
|
-
* @description Notification shown to the user whenever DevTools receives an external request.
|
|
316
|
-
*/
|
|
317
|
-
externalRequestReceived: '`DevTools` received an external request',
|
|
318
313
|
} as const;
|
|
319
314
|
const str_ = i18n.i18n.registerUIStrings('panels/timeline/TimelinePanel.ts', UIStrings);
|
|
320
315
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
@@ -407,7 +402,6 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
|
407
402
|
private traceLoadStart!: Trace.Types.Timing.Milli|null;
|
|
408
403
|
|
|
409
404
|
#traceEngineModel: Trace.TraceModel.Model;
|
|
410
|
-
#externalAIConversationData: AiAssistanceModel.ConversationHandler.ExternalPerformanceAIConversationData|null = null;
|
|
411
405
|
#sourceMapsResolver: SourceMapsResolver.SourceMapsResolver|null = null;
|
|
412
406
|
#entityMapper: Trace.EntityMapper.EntityMapper|null = null;
|
|
413
407
|
#onSourceMapsNodeNamesResolvedBound = this.#onSourceMapsNodeNamesResolved.bind(this);
|
|
@@ -976,39 +970,6 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
|
976
970
|
return this.#traceEngineModel;
|
|
977
971
|
}
|
|
978
972
|
|
|
979
|
-
getOrCreateExternalAIConversationData(): AiAssistanceModel.ConversationHandler.ExternalPerformanceAIConversationData {
|
|
980
|
-
if (!this.#externalAIConversationData) {
|
|
981
|
-
const conversationHandler = AiAssistanceModel.ConversationHandler.ConversationHandler.instance();
|
|
982
|
-
const focus = AiAssistanceModel.AIContext.getPerformanceAgentFocusFromModel(this.model);
|
|
983
|
-
if (!focus) {
|
|
984
|
-
throw new Error('could not create performance agent focus');
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
const conversation = new AiAssistanceModel.AiConversation.AiConversation({
|
|
988
|
-
type: AiAssistanceModel.AiHistoryStorage.ConversationType.PERFORMANCE,
|
|
989
|
-
data: [],
|
|
990
|
-
isReadOnly: true,
|
|
991
|
-
aidaClient: conversationHandler.aidaClient,
|
|
992
|
-
isExternal: true,
|
|
993
|
-
});
|
|
994
|
-
|
|
995
|
-
const selected = new AiAssistanceModel.PerformanceAgent.PerformanceTraceContext(focus);
|
|
996
|
-
selected.external = true;
|
|
997
|
-
|
|
998
|
-
this.#externalAIConversationData = {
|
|
999
|
-
conversationHandler,
|
|
1000
|
-
conversation,
|
|
1001
|
-
selected,
|
|
1002
|
-
};
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
return this.#externalAIConversationData;
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
invalidateExternalAIConversationData(): void {
|
|
1009
|
-
this.#externalAIConversationData = null;
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
973
|
/**
|
|
1013
974
|
* NOTE: this method only exists to enable some layout tests to be migrated to the new engine.
|
|
1014
975
|
* DO NOT use this method within DevTools. It is marked as deprecated so
|
|
@@ -3102,120 +3063,6 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
|
3102
3063
|
return trace;
|
|
3103
3064
|
}
|
|
3104
3065
|
|
|
3105
|
-
static async *
|
|
3106
|
-
handleExternalRecordRequest(): AsyncGenerator<
|
|
3107
|
-
AiAssistanceModel.AiAgent.ExternalRequestResponse, AiAssistanceModel.AiAgent.ExternalRequestResponse> {
|
|
3108
|
-
yield {
|
|
3109
|
-
type: AiAssistanceModel.AiAgent.ExternalRequestResponseType.NOTIFICATION,
|
|
3110
|
-
message: 'Recording performance trace',
|
|
3111
|
-
};
|
|
3112
|
-
TimelinePanel.instance().invalidateExternalAIConversationData();
|
|
3113
|
-
void VisualLogging.logFunctionCall('timeline.record-reload', 'external');
|
|
3114
|
-
Snackbars.Snackbar.Snackbar.show({message: i18nString(UIStrings.externalRequestReceived)});
|
|
3115
|
-
|
|
3116
|
-
const panelInstance = TimelinePanel.instance();
|
|
3117
|
-
// Given how the current UX works, it's nice to show the user the Perf
|
|
3118
|
-
// Panel so they see what's happening
|
|
3119
|
-
await UI.ViewManager.ViewManager.instance().showView('timeline');
|
|
3120
|
-
|
|
3121
|
-
function onRecordingCompleted(eventData: EventTypes[Events.RECORDING_COMPLETED]):
|
|
3122
|
-
AiAssistanceModel.AiAgent.ExternalRequestResponse {
|
|
3123
|
-
if ('errorText' in eventData) {
|
|
3124
|
-
return {
|
|
3125
|
-
type: AiAssistanceModel.AiAgent.ExternalRequestResponseType.ERROR,
|
|
3126
|
-
message: `Error running the trace: ${eventData.errorText}`,
|
|
3127
|
-
};
|
|
3128
|
-
}
|
|
3129
|
-
|
|
3130
|
-
const parsedTrace = panelInstance.model.parsedTrace(eventData.traceIndex);
|
|
3131
|
-
if (!parsedTrace || !parsedTrace.insights || parsedTrace.insights.size === 0) {
|
|
3132
|
-
return {
|
|
3133
|
-
type: AiAssistanceModel.AiAgent.ExternalRequestResponseType.ERROR,
|
|
3134
|
-
message: 'The trace was loaded successfully but no Insights were detected.',
|
|
3135
|
-
};
|
|
3136
|
-
}
|
|
3137
|
-
|
|
3138
|
-
const insightSetId = Array.from(parsedTrace.insights.keys()).find(k => k !== 'NO_NAVIGATION');
|
|
3139
|
-
if (!insightSetId) {
|
|
3140
|
-
return {
|
|
3141
|
-
type: AiAssistanceModel.AiAgent.ExternalRequestResponseType.ERROR,
|
|
3142
|
-
message: 'The trace was loaded successfully but no navigation was detected.',
|
|
3143
|
-
};
|
|
3144
|
-
}
|
|
3145
|
-
|
|
3146
|
-
const insightsForNav = parsedTrace.insights.get(insightSetId);
|
|
3147
|
-
if (!insightsForNav) {
|
|
3148
|
-
return {
|
|
3149
|
-
type: AiAssistanceModel.AiAgent.ExternalRequestResponseType.ERROR,
|
|
3150
|
-
message: 'The trace was loaded successfully but no Insights were detected.',
|
|
3151
|
-
};
|
|
3152
|
-
}
|
|
3153
|
-
|
|
3154
|
-
let responseTextForNonPassedInsights = '';
|
|
3155
|
-
// We still return info on the passed insights, but we put it at the
|
|
3156
|
-
// bottom of the response under a heading.
|
|
3157
|
-
let responseTextForPassedInsights = '';
|
|
3158
|
-
|
|
3159
|
-
// TODO(b/442392194): use PerformanceTraceFormatter summary instead.
|
|
3160
|
-
for (const insight of Object.values(insightsForNav.model)) {
|
|
3161
|
-
const focus = AiAssistanceModel.AIContext.AgentFocus.fromParsedTrace(parsedTrace);
|
|
3162
|
-
const formatter = new AiAssistanceModel.PerformanceInsightFormatter.PerformanceInsightFormatter(focus, insight);
|
|
3163
|
-
if (!formatter.insightIsSupported()) {
|
|
3164
|
-
// Not all Insights are integrated with "Ask AI" yet, let's avoid
|
|
3165
|
-
// filling up the response with those ones because there will be no
|
|
3166
|
-
// useful information.
|
|
3167
|
-
continue;
|
|
3168
|
-
}
|
|
3169
|
-
|
|
3170
|
-
const formatted = formatter.formatInsight({headingLevel: 3});
|
|
3171
|
-
|
|
3172
|
-
if (insight.state === 'pass') {
|
|
3173
|
-
responseTextForPassedInsights += `${formatted}\n\n`;
|
|
3174
|
-
continue;
|
|
3175
|
-
} else {
|
|
3176
|
-
responseTextForNonPassedInsights += `${formatted}\n\n`;
|
|
3177
|
-
}
|
|
3178
|
-
}
|
|
3179
|
-
|
|
3180
|
-
const finalText = `# Trace recording results
|
|
3181
|
-
|
|
3182
|
-
## Non-passing insights:
|
|
3183
|
-
|
|
3184
|
-
These insights highlight potential problems and opportunities to improve performance.
|
|
3185
|
-
${responseTextForNonPassedInsights}
|
|
3186
|
-
|
|
3187
|
-
## Passing insights:
|
|
3188
|
-
|
|
3189
|
-
These insights are passing, which means they are not considered to highlight considerable performance problems.
|
|
3190
|
-
${responseTextForPassedInsights}`;
|
|
3191
|
-
|
|
3192
|
-
return {
|
|
3193
|
-
type: AiAssistanceModel.AiAgent.ExternalRequestResponseType.ANSWER,
|
|
3194
|
-
message: finalText,
|
|
3195
|
-
devToolsLogs: [],
|
|
3196
|
-
};
|
|
3197
|
-
}
|
|
3198
|
-
|
|
3199
|
-
return await new Promise(resolve => {
|
|
3200
|
-
function listener(e: Common.EventTarget.EventTargetEvent<EventTypes[Events.RECORDING_COMPLETED]>): void {
|
|
3201
|
-
resolve(onRecordingCompleted(e.data));
|
|
3202
|
-
panelInstance.removeEventListener(Events.RECORDING_COMPLETED, listener);
|
|
3203
|
-
}
|
|
3204
|
-
panelInstance.addEventListener(Events.RECORDING_COMPLETED, listener);
|
|
3205
|
-
|
|
3206
|
-
panelInstance.recordReload();
|
|
3207
|
-
});
|
|
3208
|
-
}
|
|
3209
|
-
|
|
3210
|
-
static async handleExternalAnalyzeRequest(prompt: string): Promise<AsyncGenerator<
|
|
3211
|
-
AiAssistanceModel.AiAgent.ExternalRequestResponse, AiAssistanceModel.AiAgent.ExternalRequestResponse>> {
|
|
3212
|
-
const data = TimelinePanel.instance().getOrCreateExternalAIConversationData();
|
|
3213
|
-
return await data.conversationHandler.handleExternalRequest({
|
|
3214
|
-
conversationType: AiAssistanceModel.AiHistoryStorage.ConversationType.PERFORMANCE,
|
|
3215
|
-
prompt,
|
|
3216
|
-
data,
|
|
3217
|
-
});
|
|
3218
|
-
}
|
|
3219
3066
|
}
|
|
3220
3067
|
|
|
3221
3068
|
export const enum State {
|
|
@@ -200,6 +200,7 @@ export class TimelineTreeView extends
|
|
|
200
200
|
// Compact mode is used to render the tree view in a more compact UI,
|
|
201
201
|
// suitable for AI assistance widgets. It removes sidebars and toolbars.
|
|
202
202
|
#compactMode = false;
|
|
203
|
+
#maxLinkLength: number|undefined = undefined;
|
|
203
204
|
|
|
204
205
|
/**
|
|
205
206
|
* Determines if the first child in the data grid will be selected
|
|
@@ -272,6 +273,14 @@ export class TimelineTreeView extends
|
|
|
272
273
|
}
|
|
273
274
|
}
|
|
274
275
|
|
|
276
|
+
get maxLinkLength(): number|undefined {
|
|
277
|
+
return this.#maxLinkLength;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
set maxLinkLength(maxLinkLength: number|undefined) {
|
|
281
|
+
this.#maxLinkLength = maxLinkLength;
|
|
282
|
+
}
|
|
283
|
+
|
|
275
284
|
#applyCompactMode(): void {
|
|
276
285
|
if (this.#compactMode && this.dataGrid) {
|
|
277
286
|
this.splitWidget?.detach();
|
|
@@ -900,7 +909,8 @@ export class GridNode extends DataGrid.SortableDataGrid.SortableDataGridNode<Gri
|
|
|
900
909
|
const linkifier = this.treeView.linkifier;
|
|
901
910
|
const isFreshOrEnhanced =
|
|
902
911
|
Boolean(parsedTrace && Tracing.FreshRecording.Tracker.instance().recordingIsFreshOrEnhanced(parsedTrace));
|
|
903
|
-
|
|
912
|
+
const maxLength = this.treeView.maxLinkLength;
|
|
913
|
+
this.linkElement = TimelineUIUtils.linkifyTopCallFrame(event, target, linkifier, isFreshOrEnhanced, maxLength);
|
|
904
914
|
if (this.linkElement) {
|
|
905
915
|
container.createChild('div', 'activity-link').appendChild(this.linkElement);
|
|
906
916
|
}
|
|
@@ -785,7 +785,7 @@ export class TimelineUIUtils {
|
|
|
785
785
|
|
|
786
786
|
static linkifyTopCallFrame(
|
|
787
787
|
event: Trace.Types.Events.Event, target: SDK.Target.Target|null, linkifier: LegacyComponents.Linkifier.Linkifier,
|
|
788
|
-
isFreshOrEnhanced = false): Element|null {
|
|
788
|
+
isFreshOrEnhanced = false, maxLength?: number): Element|null {
|
|
789
789
|
let frame = Trace.Helpers.Trace.getZeroIndexedStackTraceInEventPayload(event)?.[0];
|
|
790
790
|
if (Trace.Types.Events.isProfileCall(event)) {
|
|
791
791
|
frame = event.callFrame;
|
|
@@ -800,6 +800,7 @@ export class TimelineUIUtils {
|
|
|
800
800
|
showColumnNumber: true,
|
|
801
801
|
columnNumber: frame.columnNumber,
|
|
802
802
|
lineNumber: frame.lineNumber,
|
|
803
|
+
maxLength,
|
|
803
804
|
};
|
|
804
805
|
if (isFreshOrEnhanced) {
|
|
805
806
|
return linkifier.maybeLinkifyConsoleCallFrame(target, frame, {showColumnNumber: true, inlineFrameIndex: 0});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Name: Dependencies sourced from the upstream `chromium` repository
|
|
2
2
|
URL: Internal
|
|
3
3
|
Version: N/A
|
|
4
|
-
Revision:
|
|
4
|
+
Revision: ed66cb5e93e6205f5231c1dcfac9b91b2aa4ffd6
|
|
5
5
|
Update Mechanism: Manual (https://crbug.com/428069060)
|
|
6
6
|
License: BSD-3-Clause
|
|
7
7
|
License File: LICENSE
|
|
@@ -34,6 +34,11 @@ const UIStrings = {
|
|
|
34
34
|
* @description Disclaimer shown in the code blocks.
|
|
35
35
|
*/
|
|
36
36
|
disclaimer: 'Use code snippets with caution',
|
|
37
|
+
/**
|
|
38
|
+
* @description The title of the button to show all lines of a code block.
|
|
39
|
+
* @example {5} PH1
|
|
40
|
+
*/
|
|
41
|
+
showAllLines: 'Show all lines ({PH1} more)',
|
|
37
42
|
} as const;
|
|
38
43
|
const str_ = i18n.i18n.registerUIStrings('ui/components/markdown_view/CodeBlock.ts', UIStrings);
|
|
39
44
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
@@ -130,6 +135,7 @@ export class CodeBlock extends HTMLElement {
|
|
|
130
135
|
#copied = false;
|
|
131
136
|
#editorState?: CodeMirror.EditorState;
|
|
132
137
|
#languageConf = new CodeMirror.Compartment();
|
|
138
|
+
#truncationConf = new CodeMirror.Compartment();
|
|
133
139
|
/**
|
|
134
140
|
* Whether to display a notice "Use code snippets with caution" in code
|
|
135
141
|
* blocks.
|
|
@@ -138,6 +144,7 @@ export class CodeBlock extends HTMLElement {
|
|
|
138
144
|
#header?: string;
|
|
139
145
|
#showCopyButton = true;
|
|
140
146
|
#citations: Citation[] = [];
|
|
147
|
+
#displayLimit = Number.MAX_VALUE;
|
|
141
148
|
|
|
142
149
|
connectedCallback(): void {
|
|
143
150
|
void this.#render();
|
|
@@ -152,6 +159,7 @@ export class CodeBlock extends HTMLElement {
|
|
|
152
159
|
CodeMirror.EditorState.readOnly.of(true),
|
|
153
160
|
CodeMirror.EditorView.lineWrapping,
|
|
154
161
|
this.#languageConf.of(CodeMirror.javascript.javascript()),
|
|
162
|
+
this.#truncationConf.of([]),
|
|
155
163
|
],
|
|
156
164
|
});
|
|
157
165
|
void this.#render();
|
|
@@ -190,6 +198,15 @@ export class CodeBlock extends HTMLElement {
|
|
|
190
198
|
this.#citations = citations;
|
|
191
199
|
}
|
|
192
200
|
|
|
201
|
+
set displayLimit(value: number) {
|
|
202
|
+
this.#displayLimit = value;
|
|
203
|
+
void this.#render();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
get displayLimit(): number {
|
|
207
|
+
return this.#displayLimit;
|
|
208
|
+
}
|
|
209
|
+
|
|
193
210
|
#onCopy(): void {
|
|
194
211
|
UI.UIUtils.copyTextToClipboard(this.#code, i18nString(UIStrings.copied));
|
|
195
212
|
this.#copied = true;
|
|
@@ -256,6 +273,9 @@ export class CodeBlock extends HTMLElement {
|
|
|
256
273
|
throw new Error('Unexpected: trying to render the text editor without editorState');
|
|
257
274
|
}
|
|
258
275
|
|
|
276
|
+
const linesCount = this.#editorState.doc.lines;
|
|
277
|
+
const isTruncated = linesCount > this.#displayLimit;
|
|
278
|
+
|
|
259
279
|
// clang-format off
|
|
260
280
|
Lit.render(
|
|
261
281
|
html`<div class='codeblock' jslog=${VisualLogging.section('code')}>
|
|
@@ -271,6 +291,19 @@ export class CodeBlock extends HTMLElement {
|
|
|
271
291
|
<div class="code">
|
|
272
292
|
<devtools-text-editor .state=${this.#editorState}></devtools-text-editor>
|
|
273
293
|
</div>
|
|
294
|
+
${isTruncated ? html`
|
|
295
|
+
<div class="show-all-container">
|
|
296
|
+
<devtools-button
|
|
297
|
+
.variant=${Buttons.Button.Variant.OUTLINED}
|
|
298
|
+
.size=${Buttons.Button.Size.SMALL}
|
|
299
|
+
.jslogContext=${'show-all'}
|
|
300
|
+
.title=${i18nString(UIStrings.showAllLines, { PH1: linesCount - this.#displayLimit })}
|
|
301
|
+
@click=${() => {
|
|
302
|
+
this.displayLimit = Number.MAX_VALUE;
|
|
303
|
+
}}
|
|
304
|
+
>${i18nString(UIStrings.showAllLines, { PH1: linesCount - this.#displayLimit })}</devtools-button>
|
|
305
|
+
</div>
|
|
306
|
+
` : Lit.nothing}
|
|
274
307
|
</div>
|
|
275
308
|
${this.#displayNotice ? this.#renderNotice() : Lit.nothing}
|
|
276
309
|
</div>`,
|
|
@@ -288,8 +321,21 @@ export class CodeBlock extends HTMLElement {
|
|
|
288
321
|
}
|
|
289
322
|
|
|
290
323
|
const language = await languageFromToken(this.#codeLang);
|
|
324
|
+
let truncationExtension: CodeMirror.Extension = [];
|
|
325
|
+
if (isTruncated) {
|
|
326
|
+
truncationExtension = CodeMirror.EditorView.decorations.of(CodeMirror.Decoration.set(
|
|
327
|
+
CodeMirror.Decoration.replace({}).range(
|
|
328
|
+
this.#editorState.doc.line(this.#displayLimit).to,
|
|
329
|
+
this.#editorState.doc.length,
|
|
330
|
+
),
|
|
331
|
+
));
|
|
332
|
+
}
|
|
333
|
+
|
|
291
334
|
editor.dispatch({
|
|
292
|
-
effects:
|
|
335
|
+
effects: [
|
|
336
|
+
this.#languageConf.reconfigure(language),
|
|
337
|
+
this.#truncationConf.reconfigure(truncationExtension),
|
|
338
|
+
],
|
|
293
339
|
});
|
|
294
340
|
}
|
|
295
341
|
}
|
|
@@ -108,3 +108,11 @@
|
|
|
108
108
|
text-decoration-line: underline;
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
|
+
|
|
112
|
+
.show-all-container {
|
|
113
|
+
display: flex;
|
|
114
|
+
justify-content: center;
|
|
115
|
+
align-items: center;
|
|
116
|
+
padding: var(--sys-size-4) 0;
|
|
117
|
+
background-color: var(--code-block-background-color); /* stylelint-disable-line plugin/use_theme_colors */
|
|
118
|
+
}
|
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
/* eslint-disable @devtools/no-imperative-dom-api, @devtools/no-lit-render-outside-of-view */
|
|
6
6
|
|
|
7
|
-
import './Toolbar.js';
|
|
8
|
-
|
|
9
7
|
import * as Common from '../../core/common/common.js';
|
|
10
8
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
11
9
|
import * as Platform from '../../core/platform/platform.js';
|
|
@@ -22,7 +20,7 @@ import tabbedPaneStyles from './tabbedPane.css.js';
|
|
|
22
20
|
import type {Toolbar} from './Toolbar.js';
|
|
23
21
|
import {Tooltip} from './Tooltip.js';
|
|
24
22
|
import {installDragHandle} from './UIUtils.js';
|
|
25
|
-
import {VBox,
|
|
23
|
+
import {registerWidgetConfig, VBox, Widget, widgetConfig, WidgetElement} from './Widget.js';
|
|
26
24
|
import {Events as ZoomManagerEvents, ZoomManager} from './ZoomManager.js';
|
|
27
25
|
|
|
28
26
|
const UIStrings = {
|
|
@@ -123,7 +121,26 @@ export class TabbedPane extends Common.ObjectWrapper.eventMixin<EventTypes, type
|
|
|
123
121
|
this.contentElement.tabIndex = -1;
|
|
124
122
|
this.setDefaultFocusedElement(this.contentElement);
|
|
125
123
|
this.#headerElement = this.contentElement.createChild('div', 'tabbed-pane-header');
|
|
124
|
+
const leftSlot = document.createElement('slot');
|
|
125
|
+
leftSlot.name = 'left';
|
|
126
|
+
leftSlot.classList.add('tabbed-pane-left-toolbar');
|
|
127
|
+
this.#headerElement.appendChild(leftSlot);
|
|
128
|
+
leftSlot.addEventListener('slotchange', () => {
|
|
129
|
+
this.#leftToolbar = leftSlot.assignedElements()[0] as Toolbar | undefined;
|
|
130
|
+
this.requestUpdate();
|
|
131
|
+
});
|
|
132
|
+
|
|
126
133
|
this.headerContentsElement = this.#headerElement.createChild('div', 'tabbed-pane-header-contents');
|
|
134
|
+
|
|
135
|
+
const rightSlot = document.createElement('slot');
|
|
136
|
+
rightSlot.name = 'right';
|
|
137
|
+
rightSlot.classList.add('tabbed-pane-right-toolbar');
|
|
138
|
+
this.#headerElement.appendChild(rightSlot);
|
|
139
|
+
rightSlot.addEventListener('slotchange', () => {
|
|
140
|
+
this.#rightToolbar = rightSlot.assignedElements()[0] as Toolbar | undefined;
|
|
141
|
+
this.requestUpdate();
|
|
142
|
+
});
|
|
143
|
+
|
|
127
144
|
this.tabSlider = document.createElement('div');
|
|
128
145
|
this.tabSlider.classList.add('tabbed-pane-tab-slider');
|
|
129
146
|
this.tabsElement = this.headerContentsElement.createChild('div', 'tabbed-pane-header-tabs');
|
|
@@ -547,6 +564,9 @@ export class TabbedPane extends Common.ObjectWrapper.eventMixin<EventTypes, type
|
|
|
547
564
|
if (existingTab) {
|
|
548
565
|
this.changeTabView(tab.id, tab.view);
|
|
549
566
|
this.changeTabTitle(tab.id, tab.title, tab.tabTooltip);
|
|
567
|
+
if (tab.jslogContext !== undefined) {
|
|
568
|
+
existingTab.jslogContext = tab.jslogContext;
|
|
569
|
+
}
|
|
550
570
|
if (tab.isCloseable !== undefined) {
|
|
551
571
|
existingTab.closeable = tab.isCloseable;
|
|
552
572
|
}
|
|
@@ -1031,6 +1051,10 @@ export class TabbedPane extends Common.ObjectWrapper.eventMixin<EventTypes, type
|
|
|
1031
1051
|
}
|
|
1032
1052
|
|
|
1033
1053
|
leftToolbar(): Toolbar {
|
|
1054
|
+
if (!this.#leftToolbar) {
|
|
1055
|
+
const leftSlot = this.#headerElement.querySelector('slot[name="left"]') as HTMLSlotElement | null;
|
|
1056
|
+
this.#leftToolbar = leftSlot?.assignedElements()[0] as Toolbar | undefined;
|
|
1057
|
+
}
|
|
1034
1058
|
if (!this.#leftToolbar) {
|
|
1035
1059
|
this.#leftToolbar = document.createElement('devtools-toolbar');
|
|
1036
1060
|
this.#leftToolbar.classList.add('tabbed-pane-left-toolbar');
|
|
@@ -1040,6 +1064,10 @@ export class TabbedPane extends Common.ObjectWrapper.eventMixin<EventTypes, type
|
|
|
1040
1064
|
}
|
|
1041
1065
|
|
|
1042
1066
|
rightToolbar(): Toolbar {
|
|
1067
|
+
if (!this.#rightToolbar) {
|
|
1068
|
+
const rightSlot = this.#headerElement.querySelector('slot[name="right"]') as HTMLSlotElement | null;
|
|
1069
|
+
this.#rightToolbar = rightSlot?.assignedElements()[0] as Toolbar | undefined;
|
|
1070
|
+
}
|
|
1043
1071
|
if (!this.#rightToolbar) {
|
|
1044
1072
|
this.#rightToolbar = document.createElement('devtools-toolbar');
|
|
1045
1073
|
this.#rightToolbar.classList.add('tabbed-pane-right-toolbar');
|
|
@@ -1191,6 +1219,10 @@ export class TabbedPaneTab {
|
|
|
1191
1219
|
return this.#jslogContext ?? (this.#id === 'console-view' ? 'console' : this.#id);
|
|
1192
1220
|
}
|
|
1193
1221
|
|
|
1222
|
+
set jslogContext(jslogContext: string|undefined) {
|
|
1223
|
+
this.#jslogContext = jslogContext;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1194
1226
|
get tabAnnotationIcon(): boolean {
|
|
1195
1227
|
return this.#tabAnnotationIcon;
|
|
1196
1228
|
}
|
|
@@ -1614,3 +1646,91 @@ export interface TabbedPaneTabDelegate {
|
|
|
1614
1646
|
closeTabs(tabbedPane: TabbedPane, ids: string[]): void;
|
|
1615
1647
|
onContextMenu(tabId: string, contextMenu: ContextMenu): void;
|
|
1616
1648
|
}
|
|
1649
|
+
|
|
1650
|
+
export class TabbedPaneElement extends WidgetElement<TabbedPane> {
|
|
1651
|
+
readonly #tabObserver = new MutationObserver(() => this.#updateTabs());
|
|
1652
|
+
|
|
1653
|
+
constructor() {
|
|
1654
|
+
super();
|
|
1655
|
+
|
|
1656
|
+
registerWidgetConfig(this, widgetConfig(element => {
|
|
1657
|
+
const widget = new TabbedPane(element as TabbedPaneElement);
|
|
1658
|
+
const slot = widget.contentElement.querySelector('slot:not([name])');
|
|
1659
|
+
if (slot) {
|
|
1660
|
+
slot.addEventListener('slotchange', () => this.#syncTabs());
|
|
1661
|
+
}
|
|
1662
|
+
widget.addEventListener(Events.TabSelected, () => {
|
|
1663
|
+
const slot =
|
|
1664
|
+
widget.contentElement.querySelector('slot:not([name])') as HTMLSlotElement | null;
|
|
1665
|
+
const nodes = slot ? slot.assignedElements() : [];
|
|
1666
|
+
for (const child of nodes) {
|
|
1667
|
+
if (child.id === widget.selectedTabId) {
|
|
1668
|
+
child.setAttribute('selected', '');
|
|
1669
|
+
} else {
|
|
1670
|
+
child.removeAttribute('selected');
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
});
|
|
1674
|
+
this.#syncTabs(widget);
|
|
1675
|
+
return widget;
|
|
1676
|
+
}));
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
override disconnectedCallback(): void {
|
|
1680
|
+
super.disconnectedCallback();
|
|
1681
|
+
this.#tabObserver.disconnect();
|
|
1682
|
+
}
|
|
1683
|
+
|
|
1684
|
+
#syncTabs(widget = this.getWidget()): void {
|
|
1685
|
+
if (!widget) {
|
|
1686
|
+
return;
|
|
1687
|
+
}
|
|
1688
|
+
this.#updateObserver(widget);
|
|
1689
|
+
this.#updateTabs(widget);
|
|
1690
|
+
}
|
|
1691
|
+
|
|
1692
|
+
#updateObserver(widget: TabbedPane): void {
|
|
1693
|
+
this.#tabObserver.disconnect();
|
|
1694
|
+
const slot = widget.contentElement.querySelector('slot:not([name])') as HTMLSlotElement | null;
|
|
1695
|
+
const nodes = slot ? slot.assignedElements() : [];
|
|
1696
|
+
for (const child of nodes) {
|
|
1697
|
+
this.#tabObserver.observe(
|
|
1698
|
+
child, {attributes: true, attributeFilter: ['title', 'jslogcontext', 'selected', 'disabled']});
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
|
|
1702
|
+
#updateTabs(widget = this.getWidget()): void {
|
|
1703
|
+
if (!widget) {
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
const tabs: TabInfo[] = [];
|
|
1707
|
+
const slot = widget.contentElement.querySelector('slot:not([name])') as HTMLSlotElement | null;
|
|
1708
|
+
const nodes = slot ? slot.assignedElements() : [];
|
|
1709
|
+
for (const child of nodes) {
|
|
1710
|
+
const id = child.id;
|
|
1711
|
+
const title = child.getAttribute('title') || '';
|
|
1712
|
+
const jslogContext = child.getAttribute('jslogcontext') || undefined;
|
|
1713
|
+
const selected = child.hasAttribute('selected');
|
|
1714
|
+
const enabled = !child.hasAttribute('disabled');
|
|
1715
|
+
const view = Widget.getOrCreateWidget(child as HTMLElement);
|
|
1716
|
+
view.setHideOnDetach();
|
|
1717
|
+
if (widget.selectedTabId !== id) {
|
|
1718
|
+
view.hideWidget();
|
|
1719
|
+
} else {
|
|
1720
|
+
view.showWidget();
|
|
1721
|
+
}
|
|
1722
|
+
tabs.push({
|
|
1723
|
+
id,
|
|
1724
|
+
title,
|
|
1725
|
+
view,
|
|
1726
|
+
jslogContext,
|
|
1727
|
+
selected,
|
|
1728
|
+
enabled,
|
|
1729
|
+
});
|
|
1730
|
+
}
|
|
1731
|
+
|
|
1732
|
+
widget.tabs = tabs;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
customElements.define('devtools-tabbed-pane', TabbedPaneElement);
|