chrome-devtools-frontend 1.0.1605219 → 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/Images/src/dots-circle.svg +10 -0
- 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 +9 -25
- 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/ConversationSummaryAgent.ts +10 -9
- package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +313 -0
- 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/PerformanceAgent.ts +19 -8
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +35 -278
- 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 +149 -49
- package/front_end/panels/ai_assistance/components/ChatView.ts +11 -15
- package/front_end/panels/ai_assistance/components/ExportForAgentsDialog.ts +13 -4
- package/front_end/panels/ai_assistance/components/chatMessage.css +53 -1
- package/front_end/panels/ai_assistance/components/chatView.css +0 -10
- package/front_end/panels/ai_assistance/components/exportForAgentsDialog.css +13 -0
- package/front_end/panels/application/WebMCPView.ts +471 -25
- package/front_end/panels/application/webMCPView.css +53 -2
- package/front_end/panels/elements/ElementsTreeElement.ts +1 -1
- package/front_end/panels/elements/ElementsTreeOutline.ts +87 -0
- package/front_end/panels/elements/StylePropertiesSection.ts +0 -4
- package/front_end/panels/elements/elements.ts +3 -0
- package/front_end/panels/elements/elementsTreeOutline.css +21 -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/HeapDetachedElementsView.ts +0 -4
- package/front_end/panels/profiler/HeapProfileView.ts +0 -4
- package/front_end/panels/profiler/HeapProfilerPanel.ts +4 -13
- package/front_end/panels/profiler/HeapSnapshotProxy.ts +2 -6
- package/front_end/panels/profiler/HeapSnapshotView.ts +19 -32
- package/front_end/panels/profiler/ProfileHeader.ts +1 -15
- package/front_end/panels/profiler/ProfileTypeRegistry.ts +4 -12
- package/front_end/panels/profiler/ProfileView.ts +1 -6
- package/front_end/panels/profiler/ProfilesPanel.ts +60 -7
- package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +27 -22
- package/front_end/panels/timeline/EventsTimelineTreeView.ts +1 -1
- package/front_end/panels/timeline/ThirdPartyTreeView.ts +2 -2
- package/front_end/panels/timeline/TimelinePanel.ts +11 -153
- package/front_end/panels/timeline/TimelineTreeView.ts +147 -48
- package/front_end/panels/timeline/TimelineUIUtils.ts +2 -1
- package/front_end/panels/timeline/components/CWVMetrics.ts +18 -2
- package/front_end/panels/timeline/timeline-meta.ts +11 -0
- package/front_end/panels/timeline/utils/Helpers.ts +5 -0
- 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/FilterBar.ts +2 -0
- package/front_end/ui/legacy/SplitWidget.ts +33 -27
- package/front_end/ui/legacy/TabbedPane.ts +123 -3
- package/front_end/ui/legacy/Widget.ts +95 -48
- 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 +6 -1
- package/package.json +1 -1
- package/front_end/core/sdk/WebMCPModel.ts +0 -104
- package/front_end/models/ai_assistance/ConversationHandler.ts +0 -325
|
@@ -62,8 +62,6 @@
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
|
|
66
|
-
|
|
67
65
|
.link {
|
|
68
66
|
color: var(--text-link);
|
|
69
67
|
text-decoration: underline;
|
|
@@ -411,11 +409,3 @@ main {
|
|
|
411
409
|
padding-bottom: var(--sys-size-1);
|
|
412
410
|
}
|
|
413
411
|
}
|
|
414
|
-
|
|
415
|
-
.export-for-agents-button {
|
|
416
|
-
/* Bottom margin to ensure it's not right up against the chat box when
|
|
417
|
-
* the conversation requires a scrollbar */
|
|
418
|
-
margin: 0 auto var(--sys-size-8) auto;
|
|
419
|
-
display: block;
|
|
420
|
-
|
|
421
|
-
}
|
|
@@ -63,6 +63,19 @@
|
|
|
63
63
|
border: none;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
main {
|
|
67
|
+
position: relative;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.prompt-loading {
|
|
71
|
+
position: absolute;
|
|
72
|
+
padding: var(--sys-size-5);
|
|
73
|
+
display: flex;
|
|
74
|
+
align-items: center;
|
|
75
|
+
justify-content: flex-start;
|
|
76
|
+
gap: var(--sys-size-5);
|
|
77
|
+
}
|
|
78
|
+
|
|
66
79
|
.export-for-agents-dialog .disclaimer {
|
|
67
80
|
margin-top: var(--sys-size-5);
|
|
68
81
|
font: var(--sys-typescale-body4-regular);
|
|
@@ -4,16 +4,24 @@
|
|
|
4
4
|
|
|
5
5
|
import '../../ui/components/icon_button/icon_button.js';
|
|
6
6
|
import '../../ui/components/lists/lists.js';
|
|
7
|
+
import '../../ui/legacy/components/data_grid/data_grid.js';
|
|
7
8
|
import '../../ui/legacy/legacy.js';
|
|
8
9
|
|
|
9
10
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
11
|
+
import * as Platform from '../../core/platform/platform.js';
|
|
10
12
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
11
|
-
import
|
|
13
|
+
import * as Protocol from '../../generated/protocol.js';
|
|
14
|
+
import * as WebMCP from '../../models/web_mcp/web_mcp.js';
|
|
15
|
+
import * as Adorners from '../../ui/components/adorners/adorners.js';
|
|
16
|
+
import * as Buttons from '../../ui/components/buttons/buttons.js';
|
|
17
|
+
import type * as IconButton from '../../ui/components/icon_button/icon_button.js';
|
|
12
18
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
13
19
|
import {
|
|
20
|
+
Directives,
|
|
14
21
|
html,
|
|
15
22
|
render,
|
|
16
23
|
} from '../../ui/lit/lit.js';
|
|
24
|
+
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
17
25
|
|
|
18
26
|
import webMCPViewStyles from './webMCPView.css.js';
|
|
19
27
|
|
|
@@ -38,25 +46,338 @@ const UIStrings = {
|
|
|
38
46
|
/**
|
|
39
47
|
* @description Text to display when no calls have been made
|
|
40
48
|
*/
|
|
41
|
-
noCallsPlaceholder: 'Start interacting with your `WebMCP` agent to see real-time tool calls and executions here.'
|
|
49
|
+
noCallsPlaceholder: 'Start interacting with your `WebMCP` agent to see real-time tool calls and executions here.',
|
|
50
|
+
/**
|
|
51
|
+
* @description Text for the name of a tool call
|
|
52
|
+
*/
|
|
53
|
+
name: 'Name',
|
|
54
|
+
/**
|
|
55
|
+
* @description Text for the status of a tool call
|
|
56
|
+
*/
|
|
57
|
+
status: 'Status',
|
|
58
|
+
/**
|
|
59
|
+
* @description Text for the input of a tool call
|
|
60
|
+
*/
|
|
61
|
+
input: 'Input',
|
|
62
|
+
/**
|
|
63
|
+
* @description Text for the output of a tool call
|
|
64
|
+
*/
|
|
65
|
+
output: 'Output',
|
|
66
|
+
/**
|
|
67
|
+
* @description Text for the status of a tool call that is in progress
|
|
68
|
+
*/
|
|
69
|
+
inProgress: 'In Progress',
|
|
70
|
+
/**
|
|
71
|
+
* @description Tooltip for the clear log button
|
|
72
|
+
*/
|
|
73
|
+
clearLog: 'Clear log',
|
|
74
|
+
/**
|
|
75
|
+
* @description Placeholder for the filter input
|
|
76
|
+
*/
|
|
77
|
+
filter: 'Filter',
|
|
78
|
+
/**
|
|
79
|
+
* @description Tooltip for the tool types dropdown
|
|
80
|
+
*/
|
|
81
|
+
toolTypes: 'Tool types',
|
|
82
|
+
/**
|
|
83
|
+
* @description Tooltip for the status types dropdown
|
|
84
|
+
*/
|
|
85
|
+
statusTypes: 'Status types',
|
|
86
|
+
/**
|
|
87
|
+
* @description Tooltip for the clear filters button
|
|
88
|
+
*/
|
|
89
|
+
clearFilters: 'Clear filters',
|
|
90
|
+
/**
|
|
91
|
+
* @description Filter option for imperative tools
|
|
92
|
+
*/
|
|
93
|
+
imperative: 'Imperative',
|
|
94
|
+
/**
|
|
95
|
+
* @description Filter option for declarative tools
|
|
96
|
+
*/
|
|
97
|
+
declarative: 'Declarative',
|
|
98
|
+
/**
|
|
99
|
+
* @description Text for the status of a tool call that has failed
|
|
100
|
+
*/
|
|
101
|
+
error: 'Error',
|
|
102
|
+
/**
|
|
103
|
+
* @description Text for the status of a tool call that was canceled
|
|
104
|
+
*/
|
|
105
|
+
canceled: 'Canceled',
|
|
106
|
+
/**
|
|
107
|
+
* @description Text for the status of a tool call that succeeded
|
|
108
|
+
*/
|
|
109
|
+
success: 'Success',
|
|
110
|
+
/**
|
|
111
|
+
* @description Text for the status of a tool call that has failed
|
|
112
|
+
*/
|
|
113
|
+
pending: 'In Progress',
|
|
114
|
+
/**
|
|
115
|
+
* @description Text for the total number of tool calls
|
|
116
|
+
* @example {2} PH1
|
|
117
|
+
*/
|
|
118
|
+
totalCalls: '{PH1} Total calls',
|
|
119
|
+
/**
|
|
120
|
+
* @description Text for the number of failed tool calls
|
|
121
|
+
* @example {1} PH1
|
|
122
|
+
*/
|
|
123
|
+
failed: '{PH1} Failed',
|
|
124
|
+
/**
|
|
125
|
+
* @description Text for the number of canceled tool calls
|
|
126
|
+
* @example {1} PH1
|
|
127
|
+
*/
|
|
128
|
+
canceledCount: '{PH1} Canceled',
|
|
129
|
+
/**
|
|
130
|
+
* @description Text for the number of in progress tool calls
|
|
131
|
+
* @example {1} PH1
|
|
132
|
+
*/
|
|
133
|
+
inProgressCount: '{PH1} In Progress',
|
|
42
134
|
} as const;
|
|
43
135
|
const str_ = i18n.i18n.registerUIStrings('panels/application/WebMCPView.ts', UIStrings);
|
|
44
136
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
45
137
|
|
|
138
|
+
export interface FilterState {
|
|
139
|
+
text: string;
|
|
140
|
+
toolTypes?: {
|
|
141
|
+
imperative?: boolean,
|
|
142
|
+
declarative?: boolean,
|
|
143
|
+
};
|
|
144
|
+
statusTypes?: {
|
|
145
|
+
success?: boolean,
|
|
146
|
+
error?: boolean,
|
|
147
|
+
pending?: boolean,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface FilterMenuButton {
|
|
152
|
+
button: UI.Toolbar.ToolbarMenuButton;
|
|
153
|
+
setCount: (count: number) => void;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export interface FilterMenuButtons {
|
|
157
|
+
toolTypes: FilterMenuButton;
|
|
158
|
+
statusTypes: FilterMenuButton;
|
|
159
|
+
}
|
|
46
160
|
export interface ViewInput {
|
|
47
|
-
tools:
|
|
161
|
+
tools: WebMCP.WebMCPModel.Tool[];
|
|
162
|
+
filters: FilterState;
|
|
163
|
+
filterButtons: FilterMenuButtons;
|
|
164
|
+
onClearLogClick: () => void;
|
|
165
|
+
onFilterChange: (filters: FilterState) => void;
|
|
166
|
+
toolCalls: WebMCP.WebMCPModel.Call[];
|
|
48
167
|
}
|
|
49
168
|
|
|
169
|
+
export function filterToolCalls(
|
|
170
|
+
toolCalls: WebMCP.WebMCPModel.Call[], filterState: FilterState): WebMCP.WebMCPModel.Call[] {
|
|
171
|
+
let filtered = [...toolCalls];
|
|
172
|
+
|
|
173
|
+
const statusTypes = filterState.statusTypes;
|
|
174
|
+
if (statusTypes) {
|
|
175
|
+
filtered = filtered.filter(call => {
|
|
176
|
+
const {success, error, pending} = statusTypes;
|
|
177
|
+
if (success && call.result?.status === Protocol.WebMCP.InvocationStatus.Success) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
if (error && call.result?.status === Protocol.WebMCP.InvocationStatus.Error) {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
if (pending && call.result === undefined) {
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
return false;
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const toolTypes = filterState.toolTypes;
|
|
191
|
+
if (toolTypes) {
|
|
192
|
+
filtered = filtered.filter(call => {
|
|
193
|
+
const {imperative, declarative} = toolTypes;
|
|
194
|
+
if (imperative && !call.tool.isDeclarative) {
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
if (declarative && call.tool.isDeclarative) {
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
return false;
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (filterState.text) {
|
|
205
|
+
const regex = Platform.StringUtilities.createPlainTextSearchRegex(filterState.text, 'i');
|
|
206
|
+
filtered = filtered.filter(call => {
|
|
207
|
+
return regex.test(call.tool.name) || regex.test(call.input) ||
|
|
208
|
+
(call.result?.output && regex.test(JSON.stringify(call.result.output))) ||
|
|
209
|
+
(call.result?.errorText && regex.test(call.result.errorText));
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return filtered;
|
|
214
|
+
}
|
|
50
215
|
export type View = (input: ViewInput, output: object, target: HTMLElement) => void;
|
|
216
|
+
function calculateToolStats(calls: WebMCP.WebMCPModel.Call[]):
|
|
217
|
+
{total: number, success: number, failed: number, canceled: number, inProgress: number} {
|
|
218
|
+
let total = 0, success = 0, failed = 0, canceled = 0, inProgress = 0;
|
|
219
|
+
for (const call of calls) {
|
|
220
|
+
total++;
|
|
221
|
+
if (call.result?.status === Protocol.WebMCP.InvocationStatus.Error) {
|
|
222
|
+
failed++;
|
|
223
|
+
} else if (call.result?.status === Protocol.WebMCP.InvocationStatus.Canceled) {
|
|
224
|
+
canceled++;
|
|
225
|
+
} else if (call.result?.status === Protocol.WebMCP.InvocationStatus.Success) {
|
|
226
|
+
success++;
|
|
227
|
+
} else if (call.result === undefined) {
|
|
228
|
+
inProgress++;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return {total, success, failed, canceled, inProgress};
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function getIconGroupsFromStats(toolStats: ReturnType<typeof calculateToolStats>):
|
|
235
|
+
IconButton.IconButton.IconWithTextData[] {
|
|
236
|
+
const groups = [];
|
|
237
|
+
if (toolStats.success > 0) {
|
|
238
|
+
groups.push({
|
|
239
|
+
iconName: 'check-circle',
|
|
240
|
+
iconColor: 'var(--sys-color-green)',
|
|
241
|
+
iconWidth: '16px',
|
|
242
|
+
iconHeight: '16px',
|
|
243
|
+
text: String(toolStats.success),
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
if (toolStats.failed > 0) {
|
|
247
|
+
groups.push({
|
|
248
|
+
iconName: 'cross-circle-filled',
|
|
249
|
+
iconColor: 'var(--sys-color-error)',
|
|
250
|
+
iconWidth: '16px',
|
|
251
|
+
iconHeight: '16px',
|
|
252
|
+
text: String(toolStats.failed),
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
if (toolStats.canceled > 0) {
|
|
256
|
+
groups.push({
|
|
257
|
+
iconName: 'record-stop',
|
|
258
|
+
iconColor: 'var(--sys-color-on-surface-light)',
|
|
259
|
+
iconWidth: '16px',
|
|
260
|
+
iconHeight: '16px',
|
|
261
|
+
text: String(toolStats.canceled),
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
if (toolStats.inProgress > 0) {
|
|
265
|
+
groups.push({
|
|
266
|
+
iconName: 'dots-circle',
|
|
267
|
+
iconWidth: '16px',
|
|
268
|
+
iconHeight: '16px',
|
|
269
|
+
text: String(toolStats.inProgress),
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
return groups;
|
|
273
|
+
}
|
|
51
274
|
export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
52
275
|
const tools = input.tools;
|
|
276
|
+
const stats = calculateToolStats(input.toolCalls);
|
|
277
|
+
const isFilterActive =
|
|
278
|
+
Boolean(input.filters.text) || Boolean(input.filters.toolTypes) || Boolean(input.filters.statusTypes);
|
|
279
|
+
const iconName = (call: WebMCP.WebMCPModel.Call): string => {
|
|
280
|
+
switch (call.result?.status) {
|
|
281
|
+
case Protocol.WebMCP.InvocationStatus.Error:
|
|
282
|
+
return 'cross-circle-filled';
|
|
283
|
+
case Protocol.WebMCP.InvocationStatus.Canceled:
|
|
284
|
+
return 'record-stop';
|
|
285
|
+
case undefined:
|
|
286
|
+
return 'dots-circle';
|
|
287
|
+
default:
|
|
288
|
+
return '';
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
const statusString = (call: WebMCP.WebMCPModel.Call): string => {
|
|
292
|
+
switch (call.result?.status) {
|
|
293
|
+
case Protocol.WebMCP.InvocationStatus.Error:
|
|
294
|
+
return i18nString(UIStrings.error);
|
|
295
|
+
case Protocol.WebMCP.InvocationStatus.Canceled:
|
|
296
|
+
return i18nString(UIStrings.canceled);
|
|
297
|
+
case Protocol.WebMCP.InvocationStatus.Success:
|
|
298
|
+
return i18nString(UIStrings.success);
|
|
299
|
+
default:
|
|
300
|
+
return i18nString(UIStrings.inProgress);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
53
303
|
// clang-format off
|
|
54
304
|
render(html`
|
|
55
305
|
<style>${webMCPViewStyles}</style>
|
|
306
|
+
<style>${UI.FilterBar.filterStyles}</style>
|
|
56
307
|
<devtools-split-view class="webmcp-view" direction="row" sidebar-position="second" name="webmcp-split-view">
|
|
57
308
|
<div slot="main" class="call-log">
|
|
309
|
+
<div class="webmcp-toolbar-container" role="toolbar" jslog=${VisualLogging.toolbar()}>
|
|
310
|
+
<devtools-toolbar class="webmcp-toolbar" role="presentation" wrappable>
|
|
311
|
+
<devtools-button title=${i18nString(UIStrings.clearLog)}
|
|
312
|
+
.iconName=${'clear'}
|
|
313
|
+
.variant=${Buttons.Button.Variant.TOOLBAR}
|
|
314
|
+
@click=${input.onClearLogClick}></devtools-button>
|
|
315
|
+
<div class="toolbar-divider"></div>
|
|
316
|
+
<devtools-toolbar-input type="filter"
|
|
317
|
+
placeholder=${i18nString(UIStrings.filter)}
|
|
318
|
+
.value=${input.filters.text}
|
|
319
|
+
@change=${(e: CustomEvent<string>) =>
|
|
320
|
+
input.onFilterChange({...input.filters, text: e.detail})}>
|
|
321
|
+
</devtools-toolbar-input>
|
|
322
|
+
<div class="toolbar-divider"></div>
|
|
323
|
+
${input.filterButtons.toolTypes.button.element}
|
|
324
|
+
<div class="toolbar-divider"></div>
|
|
325
|
+
${input.filterButtons.statusTypes.button.element}
|
|
326
|
+
<div class="toolbar-spacer"></div>
|
|
327
|
+
<devtools-button title=${i18nString(UIStrings.clearFilters)}
|
|
328
|
+
.iconName=${'filter-clear'}
|
|
329
|
+
.variant=${Buttons.Button.Variant.TOOLBAR}
|
|
330
|
+
@click=${() => input.onFilterChange({text: ''})}
|
|
331
|
+
?hidden=${!isFilterActive}></devtools-button>
|
|
332
|
+
</devtools-toolbar>
|
|
333
|
+
</div>
|
|
334
|
+
${input.toolCalls.length > 0 ? html`
|
|
335
|
+
<devtools-data-grid striped>
|
|
336
|
+
<table>
|
|
337
|
+
<tr>
|
|
338
|
+
<th id="name" weight="20">
|
|
339
|
+
${i18nString(UIStrings.name)}
|
|
340
|
+
</th>
|
|
341
|
+
<th id="status" weight="20">${i18nString(UIStrings.status)}</th>
|
|
342
|
+
<th id="input" weight="30">${i18nString(UIStrings.input)}</th>
|
|
343
|
+
<th id="output" weight="30">${i18nString(UIStrings.output)}</th>
|
|
344
|
+
</tr>
|
|
345
|
+
${Directives.repeat(input.toolCalls, call => call.invocationId + '-' + (call.result?.status ?? ''), call => html`
|
|
346
|
+
<tr class=${
|
|
347
|
+
call.result?.status === Protocol.WebMCP.InvocationStatus.Error ? 'status-error' :
|
|
348
|
+
call.result?.status === Protocol.WebMCP.InvocationStatus.Canceled ? 'status-cancelled' : ''
|
|
349
|
+
}>
|
|
350
|
+
<style>${webMCPViewStyles}</style>
|
|
351
|
+
<td>${call.tool.name}</td>
|
|
352
|
+
<td>
|
|
353
|
+
<div class="status-cell">
|
|
354
|
+
${iconName(call) ? html`<devtools-icon class="small" name=${iconName(call)}></devtools-icon>`
|
|
355
|
+
: ''}
|
|
356
|
+
<span>${statusString(call)}</span>
|
|
357
|
+
</div>
|
|
358
|
+
</td>
|
|
359
|
+
<td>${call.input}</td>
|
|
360
|
+
<td>${call.result?.output ? JSON.stringify(call.result.output) : call.result?.errorText ?? ''}</td>
|
|
361
|
+
</tr>
|
|
362
|
+
`)}
|
|
363
|
+
</table>
|
|
364
|
+
</devtools-data-grid>
|
|
365
|
+
<div class="webmcp-toolbar-container" role="toolbar">
|
|
366
|
+
<devtools-toolbar class="webmcp-toolbar" role="presentation" wrappable>
|
|
367
|
+
<span class="toolbar-text">${i18nString(UIStrings.totalCalls, {PH1: stats.total})}</span>
|
|
368
|
+
<div class="toolbar-divider"></div>
|
|
369
|
+
<span class="toolbar-text status-error-text">${i18nString(UIStrings.failed, {PH1: stats.failed})}</span>
|
|
370
|
+
<div class="toolbar-divider"></div>
|
|
371
|
+
<span class="toolbar-text status-cancelled-text">${
|
|
372
|
+
i18nString(UIStrings.canceledCount, {PH1: stats.canceled})}</span>
|
|
373
|
+
<div class="toolbar-divider"></div>
|
|
374
|
+
<span class="toolbar-text">${i18nString(UIStrings.inProgressCount, {PH1: stats.inProgress})}</span>
|
|
375
|
+
</devtools-toolbar>
|
|
376
|
+
</div>
|
|
377
|
+
` : html`
|
|
58
378
|
${UI.Widget.widget(UI.EmptyWidget.EmptyWidget, {header: i18nString(UIStrings.noCallsPlaceholderTitle),
|
|
59
|
-
|
|
379
|
+
text: i18nString(UIStrings.noCallsPlaceholder)})}
|
|
380
|
+
`}
|
|
60
381
|
</div>
|
|
61
382
|
<div slot="sidebar" class="tool-list">
|
|
62
383
|
<div class="section-title">${i18nString(UIStrings.toolRegistry)}</div>
|
|
@@ -65,14 +386,20 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
65
386
|
text: i18nString(UIStrings.noToolsPlaceholder)})}
|
|
66
387
|
` : html`
|
|
67
388
|
<devtools-list>
|
|
68
|
-
${tools.map(tool =>
|
|
389
|
+
${tools.map(tool => {
|
|
390
|
+
const toolStats = calculateToolStats(input.toolCalls.filter(c => c.tool === tool));
|
|
391
|
+
const groups = getIconGroupsFromStats(toolStats);
|
|
392
|
+
return html`
|
|
69
393
|
<div class="tool-item">
|
|
70
394
|
<div class="tool-name-container">
|
|
71
395
|
<div class="tool-name source-code">${tool.name}</div>
|
|
396
|
+
${groups.length > 0 ? html`<icon-button .data=${
|
|
397
|
+
{groups, compact: false} as IconButton.IconButton.IconButtonData}></icon-button>` : ''}
|
|
72
398
|
</div>
|
|
73
399
|
<div class="tool-description">${tool.description}</div>
|
|
74
400
|
</div>
|
|
75
|
-
|
|
401
|
+
`;
|
|
402
|
+
})}
|
|
76
403
|
</devtools-list>
|
|
77
404
|
`}
|
|
78
405
|
</div>
|
|
@@ -84,35 +411,154 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
84
411
|
export class WebMCPView extends UI.Widget.VBox {
|
|
85
412
|
readonly #view: View;
|
|
86
413
|
|
|
414
|
+
#filterState: FilterState = {
|
|
415
|
+
text: '',
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
#filterButtons: FilterMenuButtons;
|
|
419
|
+
|
|
420
|
+
static createFilterButtons(
|
|
421
|
+
onToolTypesClick: (contextMenu: UI.ContextMenu.ContextMenu) => void,
|
|
422
|
+
onStatusTypesClick: (contextMenu: UI.ContextMenu.ContextMenu) => void): FilterMenuButtons {
|
|
423
|
+
const createButton =
|
|
424
|
+
(label: string, onContextMenu: (contextMenu: UI.ContextMenu.ContextMenu) => void, jsLogContext: string):
|
|
425
|
+
FilterMenuButton => {
|
|
426
|
+
const button = new UI.Toolbar.ToolbarMenuButton(
|
|
427
|
+
onContextMenu,
|
|
428
|
+
/* isIconDropdown=*/ false, /* useSoftMenu=*/ true, jsLogContext,
|
|
429
|
+
/* iconName=*/ undefined,
|
|
430
|
+
/* keepOpen=*/ true);
|
|
431
|
+
button.setText(label);
|
|
432
|
+
|
|
433
|
+
/* eslint-disable-next-line @devtools/no-imperative-dom-api */
|
|
434
|
+
const adorner = new Adorners.Adorner.Adorner();
|
|
435
|
+
adorner.name = 'countWrapper';
|
|
436
|
+
const countElement = document.createElement('span');
|
|
437
|
+
adorner.append(countElement);
|
|
438
|
+
adorner.classList.add('active-filters-count');
|
|
439
|
+
adorner.classList.add('hidden');
|
|
440
|
+
button.setAdorner(adorner);
|
|
441
|
+
|
|
442
|
+
const setCount = (count: number): void => {
|
|
443
|
+
countElement.textContent = `${count}`;
|
|
444
|
+
count === 0 ? adorner.hide() : adorner.show();
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
return {button, setCount};
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
return {
|
|
451
|
+
toolTypes: createButton(i18nString(UIStrings.toolTypes), onToolTypesClick, 'webmcp.tool-types'),
|
|
452
|
+
statusTypes: createButton(i18nString(UIStrings.statusTypes), onStatusTypesClick, 'webmcp.status-types'),
|
|
453
|
+
};
|
|
454
|
+
}
|
|
87
455
|
constructor(target?: HTMLElement, view: View = DEFAULT_VIEW) {
|
|
88
|
-
super();
|
|
456
|
+
super(target);
|
|
89
457
|
this.#view = view;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
458
|
+
this.#filterButtons = WebMCPView.createFilterButtons(
|
|
459
|
+
this.#showToolTypesContextMenu.bind(this),
|
|
460
|
+
this.#showStatusTypesContextMenu.bind(this),
|
|
461
|
+
);
|
|
462
|
+
SDK.TargetManager.TargetManager.instance().observeModels(WebMCP.WebMCPModel.WebMCPModel, {
|
|
463
|
+
modelAdded: (model: WebMCP.WebMCPModel.WebMCPModel) => this.#webMCPModelAdded(model),
|
|
464
|
+
modelRemoved: (model: WebMCP.WebMCPModel.WebMCPModel) => this.#webMCPModelRemoved(model),
|
|
93
465
|
});
|
|
94
466
|
this.requestUpdate();
|
|
95
467
|
}
|
|
96
468
|
|
|
97
|
-
#
|
|
98
|
-
|
|
99
|
-
|
|
469
|
+
#showToolTypesContextMenu(contextMenu: UI.ContextMenu.ContextMenu): void {
|
|
470
|
+
const toggle = (key: 'imperative'|'declarative'): void => {
|
|
471
|
+
const current = this.#filterState.toolTypes ?? {};
|
|
472
|
+
const next = {...current, [key]: !current[key]};
|
|
473
|
+
let toolTypesToPass: FilterState['toolTypes'] = next;
|
|
474
|
+
if (!next.imperative && !next.declarative) {
|
|
475
|
+
toolTypesToPass = undefined;
|
|
476
|
+
}
|
|
477
|
+
this.#handleFilterChange({...this.#filterState, toolTypes: toolTypesToPass});
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
contextMenu.defaultSection().appendCheckboxItem(
|
|
481
|
+
i18nString(UIStrings.imperative), () => toggle('imperative'),
|
|
482
|
+
{checked: this.#filterState.toolTypes?.imperative ?? false, jslogContext: 'webmcp.imperative'});
|
|
483
|
+
contextMenu.defaultSection().appendCheckboxItem(
|
|
484
|
+
i18nString(UIStrings.declarative), () => toggle('declarative'),
|
|
485
|
+
{checked: this.#filterState.toolTypes?.declarative ?? false, jslogContext: 'webmcp.declarative'});
|
|
100
486
|
}
|
|
101
487
|
|
|
102
|
-
#
|
|
103
|
-
|
|
104
|
-
|
|
488
|
+
#showStatusTypesContextMenu(contextMenu: UI.ContextMenu.ContextMenu): void {
|
|
489
|
+
const toggle = (key: 'success'|'error'|'pending'): void => {
|
|
490
|
+
const current = this.#filterState.statusTypes ?? {};
|
|
491
|
+
const next = {...current, [key]: !current[key]};
|
|
492
|
+
let statusTypesToPass: FilterState['statusTypes'] = next;
|
|
493
|
+
if (!next.success && !next.error && !next.pending) {
|
|
494
|
+
statusTypesToPass = undefined;
|
|
495
|
+
}
|
|
496
|
+
this.#handleFilterChange({...this.#filterState, statusTypes: statusTypesToPass});
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
contextMenu.defaultSection().appendCheckboxItem(
|
|
500
|
+
i18nString(UIStrings.success), () => toggle('success'),
|
|
501
|
+
{checked: this.#filterState.statusTypes?.['success'] ?? false, jslogContext: 'webmcp.success'});
|
|
502
|
+
contextMenu.defaultSection().appendCheckboxItem(
|
|
503
|
+
i18nString(UIStrings.error), () => toggle('error'),
|
|
504
|
+
{checked: this.#filterState.statusTypes?.['error'] ?? false, jslogContext: 'webmcp.error'});
|
|
505
|
+
contextMenu.defaultSection().appendCheckboxItem(
|
|
506
|
+
i18nString(UIStrings.pending), () => toggle('pending'),
|
|
507
|
+
{checked: this.#filterState.statusTypes?.['pending'] ?? false, jslogContext: 'webmcp.pending'});
|
|
105
508
|
}
|
|
509
|
+
#webMCPModelAdded(model: WebMCP.WebMCPModel.WebMCPModel): void {
|
|
510
|
+
model.addEventListener(WebMCP.WebMCPModel.Events.TOOLS_ADDED, this.requestUpdate, this);
|
|
511
|
+
model.addEventListener(WebMCP.WebMCPModel.Events.TOOLS_REMOVED, this.requestUpdate, this);
|
|
512
|
+
model.addEventListener(WebMCP.WebMCPModel.Events.TOOL_INVOKED, this.requestUpdate, this);
|
|
513
|
+
model.addEventListener(WebMCP.WebMCPModel.Events.TOOL_RESPONDED, this.requestUpdate, this);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
#webMCPModelRemoved(model: WebMCP.WebMCPModel.WebMCPModel): void {
|
|
517
|
+
model.removeEventListener(WebMCP.WebMCPModel.Events.TOOLS_ADDED, this.requestUpdate, this);
|
|
518
|
+
model.removeEventListener(WebMCP.WebMCPModel.Events.TOOLS_REMOVED, this.requestUpdate, this);
|
|
519
|
+
model.removeEventListener(WebMCP.WebMCPModel.Events.TOOL_INVOKED, this.requestUpdate, this);
|
|
520
|
+
model.removeEventListener(WebMCP.WebMCPModel.Events.TOOL_RESPONDED, this.requestUpdate, this);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
#handleClearLogClick = (): void => {
|
|
524
|
+
const models = SDK.TargetManager.TargetManager.instance().models(WebMCP.WebMCPModel.WebMCPModel);
|
|
525
|
+
for (const model of models) {
|
|
526
|
+
model.clearCalls();
|
|
527
|
+
}
|
|
528
|
+
this.requestUpdate();
|
|
529
|
+
};
|
|
530
|
+
|
|
531
|
+
#handleFilterChange = (filters: FilterState): void => {
|
|
532
|
+
this.#filterState = filters;
|
|
106
533
|
|
|
534
|
+
const toolTypesCount =
|
|
535
|
+
this.#filterState.toolTypes ? Object.values(this.#filterState.toolTypes).filter(Boolean).length : 0;
|
|
536
|
+
this.#filterButtons.toolTypes.setCount(toolTypesCount);
|
|
537
|
+
|
|
538
|
+
const statusTypesCount =
|
|
539
|
+
this.#filterState.statusTypes ? Object.values(this.#filterState.statusTypes).filter(Boolean).length : 0;
|
|
540
|
+
this.#filterButtons.statusTypes.setCount(statusTypesCount);
|
|
541
|
+
|
|
542
|
+
this.requestUpdate();
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
#getTools(): WebMCP.WebMCPModel.Tool[] {
|
|
546
|
+
const models = SDK.TargetManager.TargetManager.instance().models(WebMCP.WebMCPModel.WebMCPModel);
|
|
547
|
+
const tools = models.flatMap(model => model.tools.toArray());
|
|
548
|
+
return tools.sort((a, b) => a.name.localeCompare(b.name));
|
|
549
|
+
}
|
|
107
550
|
override performUpdate(): void {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
551
|
+
const models = SDK.TargetManager.TargetManager.instance().models(WebMCP.WebMCPModel.WebMCPModel);
|
|
552
|
+
const toolCalls = models.flatMap(model => model.toolCalls);
|
|
553
|
+
const filteredCalls = filterToolCalls(toolCalls, this.#filterState);
|
|
554
|
+
const input: ViewInput = {
|
|
555
|
+
tools: this.#getTools(),
|
|
556
|
+
toolCalls: filteredCalls,
|
|
557
|
+
filters: this.#filterState,
|
|
558
|
+
filterButtons: this.#filterButtons,
|
|
559
|
+
onClearLogClick: this.#handleClearLogClick,
|
|
560
|
+
onFilterChange: this.#handleFilterChange,
|
|
561
|
+
};
|
|
562
|
+
this.#view(input, {}, this.contentElement);
|
|
117
563
|
}
|
|
118
564
|
}
|