chrome-devtools-frontend 1.0.1611099 → 1.0.1611825

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/front_end/core/host/AidaClientTypes.ts +5 -0
  2. package/front_end/core/host/AidaGcaTranslation.ts +2 -1
  3. package/front_end/core/sdk/ResourceTreeModel.ts +2 -1
  4. package/front_end/core/sdk/Target.ts +1 -3
  5. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  6. package/front_end/generated/SupportedCSSProperties.js +4 -0
  7. package/front_end/generated/protocol.ts +0 -8
  8. package/front_end/models/web_mcp/WebMCPModel.ts +73 -18
  9. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +2 -3
  10. package/front_end/panels/ai_assistance/components/ChatMessage.ts +30 -7
  11. package/front_end/panels/ai_assistance/components/ExportForAgentsDialog.ts +8 -4
  12. package/front_end/panels/ai_assistance/components/WalkthroughView.ts +6 -2
  13. package/front_end/panels/ai_assistance/components/exportForAgentsDialog.css +1 -1
  14. package/front_end/panels/ai_assistance/components/walkthroughView.css +18 -17
  15. package/front_end/panels/application/WebMCPView.ts +97 -6
  16. package/front_end/panels/application/webMCPView.css +7 -1
  17. package/front_end/panels/console/ConsolePinPane.ts +6 -2
  18. package/front_end/panels/sources/BreakpointEditDialog.ts +6 -0
  19. package/front_end/third_party/chromium/README.chromium +1 -1
  20. package/front_end/third_party/lighthouse/README.chromium +2 -2
  21. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +5635 -1498
  22. package/front_end/third_party/lighthouse/locales/en-GB.json +2 -2
  23. package/front_end/third_party/lighthouse/locales/en-US.json +14 -2
  24. package/front_end/third_party/lighthouse/locales/en-XL.json +12 -0
  25. package/front_end/third_party/lighthouse/report/bundle.d.ts +4 -4
  26. package/front_end/third_party/lighthouse/report/bundle.js +30 -3
  27. package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +2 -2
  28. package/front_end/ui/components/markdown_view/CodeBlock.ts +6 -0
  29. package/front_end/ui/legacy/components/settings_ui/SettingsUI.ts +83 -68
  30. package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
  31. package/front_end/ui/visual_logging/LoggingDriver.ts +3 -3
  32. package/mcp/mcp.ts +2 -1
  33. package/package.json +1 -1
@@ -384,10 +384,15 @@ export interface FactualityMetadata {
384
384
  facts: FactualityFact[];
385
385
  }
386
386
 
387
+ export interface InferenceOptionMetadata {
388
+ modelId: string;
389
+ }
390
+
387
391
  export interface ResponseMetadata {
388
392
  rpcGlobalId?: RpcGlobalId;
389
393
  attributionMetadata?: AttributionMetadata;
390
394
  factualityMetadata?: FactualityMetadata;
395
+ inferenceOptionMetadata?: InferenceOptionMetadata;
391
396
  }
392
397
 
393
398
  export interface DoConversationResponse {
@@ -360,7 +360,7 @@ function gcaCandidateToAidaGenerationSample(candidate: GCA.Candidate): AIDA.Gene
360
360
 
361
361
  function convertAidaFactsToGcaContent(facts: AIDA.RequestFact[]): GCA.Content {
362
362
  return {
363
- role: 'model',
363
+ role: 'user',
364
364
  parts: facts.map(fact => {
365
365
  return {text: `[source: ${fact.metadata.source}] ${fact.text}`};
366
366
  }),
@@ -454,6 +454,7 @@ export function gcaChunkResponseToAidaChunkResponse(response: GCA.GenerateConten
454
454
  const parts = candidate?.content?.parts || [];
455
455
  const metadata: AIDA.ResponseMetadata = {
456
456
  rpcGlobalId: response.responseId,
457
+ inferenceOptionMetadata: {modelId: response.modelVersion}
457
458
  };
458
459
 
459
460
  if (candidate?.citationMetadata?.citations) {
@@ -858,7 +858,8 @@ export class ResourceTreeFrame {
858
858
  * https://chromium.googlesource.com/chromium/src/+/HEAD/docs/frame_trees.md
859
859
  */
860
860
  isPrimaryFrame(): boolean {
861
- return !this.#sameTargetParentFrame && this.#model.target() === TargetManager.instance().primaryPageTarget();
861
+ return !this.#sameTargetParentFrame &&
862
+ this.#model.target() === this.#model.target().targetManager().primaryPageTarget();
862
863
  }
863
864
 
864
865
  removeChildFrame(frame: ResourceTreeFrame, isSwap: boolean): void {
@@ -70,9 +70,7 @@ export class Target extends ProtocolClient.InspectorBackend.TargetBase {
70
70
  this.#capabilitiesMask = Capability.JS | Capability.LOG | Capability.NETWORK | Capability.TARGET |
71
71
  Capability.INSPECTOR | Capability.IO | Capability.EVENT_BREAKPOINTS;
72
72
  if (parentTarget?.type() !== Type.FRAME) {
73
- // TODO(crbug.com/406991275): This should also grant the `STORAGE` capability, but first the
74
- // crashers in https://crbug.com/466134219 have to be resolved.
75
- this.#capabilitiesMask |= Capability.BROWSER;
73
+ this.#capabilitiesMask |= Capability.BROWSER | Capability.STORAGE;
76
74
  }
77
75
  break;
78
76
  case Type.SHARED_WORKER:
@@ -895,7 +895,7 @@ inspectorBackend.registerCommand("Network.enableReportingApi", [{"name": "enable
895
895
  inspectorBackend.registerCommand("Network.enableDeviceBoundSessions", [{"name": "enable", "type": "boolean", "optional": false, "description": "Whether to enable or disable events.", "typeRef": null}], [], "Sets up tracking device bound sessions and fetching of initial set of sessions.");
896
896
  inspectorBackend.registerCommand("Network.fetchSchemefulSite", [{"name": "origin", "type": "string", "optional": false, "description": "The URL origin.", "typeRef": null}], ["schemefulSite"], "Fetches the schemeful site for a specific origin.");
897
897
  inspectorBackend.registerCommand("Network.loadNetworkResource", [{"name": "frameId", "type": "string", "optional": true, "description": "Frame id to get the resource for. Mandatory for frame targets, and should be omitted for worker targets.", "typeRef": "Page.FrameId"}, {"name": "url", "type": "string", "optional": false, "description": "URL of the resource to get content for.", "typeRef": null}, {"name": "options", "type": "object", "optional": false, "description": "Options for the request.", "typeRef": "Network.LoadNetworkResourceOptions"}], ["resource"], "Fetches the resource and returns the content.");
898
- inspectorBackend.registerCommand("Network.setCookieControls", [{"name": "enableThirdPartyCookieRestriction", "type": "boolean", "optional": false, "description": "Whether 3pc restriction is enabled.", "typeRef": null}, {"name": "disableThirdPartyCookieMetadata", "type": "boolean", "optional": false, "description": "Whether 3pc grace period exception should be enabled; false by default.", "typeRef": null}, {"name": "disableThirdPartyCookieHeuristics", "type": "boolean", "optional": false, "description": "Whether 3pc heuristics exceptions should be enabled; false by default.", "typeRef": null}], [], "Sets Controls for third-party cookie access Page reload is required before the new cookie behavior will be observed");
898
+ inspectorBackend.registerCommand("Network.setCookieControls", [{"name": "enableThirdPartyCookieRestriction", "type": "boolean", "optional": false, "description": "Whether 3pc restriction is enabled.", "typeRef": null}], [], "Sets Controls for third-party cookie access Page reload is required before the new cookie behavior will be observed");
899
899
  inspectorBackend.registerType("Network.ResourceTiming", [{"name": "requestTime", "type": "number", "optional": false, "description": "Timing's requestTime is a baseline in seconds, while the other numbers are ticks in milliseconds relatively to this requestTime.", "typeRef": null}, {"name": "proxyStart", "type": "number", "optional": false, "description": "Started resolving proxy.", "typeRef": null}, {"name": "proxyEnd", "type": "number", "optional": false, "description": "Finished resolving proxy.", "typeRef": null}, {"name": "dnsStart", "type": "number", "optional": false, "description": "Started DNS address resolve.", "typeRef": null}, {"name": "dnsEnd", "type": "number", "optional": false, "description": "Finished DNS address resolve.", "typeRef": null}, {"name": "connectStart", "type": "number", "optional": false, "description": "Started connecting to the remote host.", "typeRef": null}, {"name": "connectEnd", "type": "number", "optional": false, "description": "Connected to the remote host.", "typeRef": null}, {"name": "sslStart", "type": "number", "optional": false, "description": "Started SSL handshake.", "typeRef": null}, {"name": "sslEnd", "type": "number", "optional": false, "description": "Finished SSL handshake.", "typeRef": null}, {"name": "workerStart", "type": "number", "optional": false, "description": "Started running ServiceWorker.", "typeRef": null}, {"name": "workerReady", "type": "number", "optional": false, "description": "Finished Starting ServiceWorker.", "typeRef": null}, {"name": "workerFetchStart", "type": "number", "optional": false, "description": "Started fetch event.", "typeRef": null}, {"name": "workerRespondWithSettled", "type": "number", "optional": false, "description": "Settled fetch event respondWith promise.", "typeRef": null}, {"name": "workerRouterEvaluationStart", "type": "number", "optional": true, "description": "Started ServiceWorker static routing source evaluation.", "typeRef": null}, {"name": "workerCacheLookupStart", "type": "number", "optional": true, "description": "Started cache lookup when the source was evaluated to `cache`.", "typeRef": null}, {"name": "sendStart", "type": "number", "optional": false, "description": "Started sending request.", "typeRef": null}, {"name": "sendEnd", "type": "number", "optional": false, "description": "Finished sending request.", "typeRef": null}, {"name": "pushStart", "type": "number", "optional": false, "description": "Time the server started pushing request.", "typeRef": null}, {"name": "pushEnd", "type": "number", "optional": false, "description": "Time the server finished pushing request.", "typeRef": null}, {"name": "receiveHeadersStart", "type": "number", "optional": false, "description": "Started receiving response headers.", "typeRef": null}, {"name": "receiveHeadersEnd", "type": "number", "optional": false, "description": "Finished receiving response headers.", "typeRef": null}]);
900
900
  inspectorBackend.registerType("Network.PostDataEntry", [{"name": "bytes", "type": "string", "optional": true, "description": "", "typeRef": null}]);
901
901
  inspectorBackend.registerType("Network.Request", [{"name": "url", "type": "string", "optional": false, "description": "Request URL (without fragment).", "typeRef": null}, {"name": "urlFragment", "type": "string", "optional": true, "description": "Fragment of the requested URL starting with hash, if present.", "typeRef": null}, {"name": "method", "type": "string", "optional": false, "description": "HTTP request method.", "typeRef": null}, {"name": "headers", "type": "object", "optional": false, "description": "HTTP request headers.", "typeRef": "Network.Headers"}, {"name": "postData", "type": "string", "optional": true, "description": "HTTP POST request data. Use postDataEntries instead.", "typeRef": null}, {"name": "hasPostData", "type": "boolean", "optional": true, "description": "True when the request has POST data. Note that postData might still be omitted when this flag is true when the data is too long.", "typeRef": null}, {"name": "postDataEntries", "type": "array", "optional": true, "description": "Request body elements (post data broken into individual entries).", "typeRef": "Network.PostDataEntry"}, {"name": "mixedContentType", "type": "string", "optional": true, "description": "The mixed content type of the request.", "typeRef": "Security.MixedContentType"}, {"name": "initialPriority", "type": "string", "optional": false, "description": "Priority of the resource request at the time request is sent.", "typeRef": "Network.ResourcePriority"}, {"name": "referrerPolicy", "type": "string", "optional": false, "description": "The referrer policy of the request, as defined in https://www.w3.org/TR/referrer-policy/", "typeRef": null}, {"name": "isLinkPreload", "type": "boolean", "optional": true, "description": "Whether is loaded via link preload.", "typeRef": null}, {"name": "trustTokenParams", "type": "object", "optional": true, "description": "Set for requests when the TrustToken API is used. Contains the parameters passed by the developer (e.g. via \\\"fetch\\\") as understood by the backend.", "typeRef": "Network.TrustTokenParams"}, {"name": "isSameSite", "type": "boolean", "optional": true, "description": "True if this resource request is considered to be the 'same site' as the request corresponding to the main frame.", "typeRef": null}, {"name": "isAdRelated", "type": "boolean", "optional": true, "description": "True when the resource request is ad-related.", "typeRef": null}]);
@@ -3493,6 +3493,7 @@ export const generatedProperties = [
3493
3493
  {
3494
3494
  "keywords": [
3495
3495
  "auto",
3496
+ "chain",
3496
3497
  "contain",
3497
3498
  "none"
3498
3499
  ],
@@ -3501,6 +3502,7 @@ export const generatedProperties = [
3501
3502
  {
3502
3503
  "keywords": [
3503
3504
  "auto",
3505
+ "chain",
3504
3506
  "contain",
3505
3507
  "none"
3506
3508
  ],
@@ -6635,6 +6637,7 @@ export const generatedPropertyValues = {
6635
6637
  "overscroll-behavior-x": {
6636
6638
  "values": [
6637
6639
  "auto",
6640
+ "chain",
6638
6641
  "contain",
6639
6642
  "none"
6640
6643
  ]
@@ -6642,6 +6645,7 @@ export const generatedPropertyValues = {
6642
6645
  "overscroll-behavior-y": {
6643
6646
  "values": [
6644
6647
  "auto",
6648
+ "chain",
6645
6649
  "contain",
6646
6650
  "none"
6647
6651
  ]
@@ -12584,14 +12584,6 @@ export namespace Network {
12584
12584
  * Whether 3pc restriction is enabled.
12585
12585
  */
12586
12586
  enableThirdPartyCookieRestriction: boolean;
12587
- /**
12588
- * Whether 3pc grace period exception should be enabled; false by default.
12589
- */
12590
- disableThirdPartyCookieMetadata: boolean;
12591
- /**
12592
- * Whether 3pc heuristics exceptions should be enabled; false by default.
12593
- */
12594
- disableThirdPartyCookieHeuristics: boolean;
12595
12587
  }
12596
12588
 
12597
12589
  /**
@@ -7,7 +7,7 @@ import * as SDK from '../../core/sdk/sdk.js';
7
7
  import type * as ProtocolProxyApi from '../../generated/protocol-proxy-api.js';
8
8
  import type * as Protocol from '../../generated/protocol.js';
9
9
  import * as Bindings from '../bindings/bindings.js';
10
- import type * as StackTrace from '../stack_trace/stack_trace.js';
10
+ import * as StackTrace from '../stack_trace/stack_trace.js';
11
11
 
12
12
  export const enum Events {
13
13
  TOOLS_ADDED = 'ToolsAdded',
@@ -16,16 +16,78 @@ export const enum Events {
16
16
  TOOL_RESPONDED = 'ToolResponded',
17
17
  }
18
18
 
19
+ export interface ExceptionDetails {
20
+ readonly error: SDK.RemoteObject.RemoteObject;
21
+ readonly description: string;
22
+ readonly frames: StackTrace.ErrorStackParser.ParsedErrorFrame[];
23
+ readonly cause?: ExceptionDetails;
24
+ }
25
+
26
+ export class Result {
27
+ readonly status: Protocol.WebMCP.InvocationStatus;
28
+ readonly output?: unknown;
29
+ readonly errorText?: string;
30
+ // TODO(crbug.com/494516094) Clean this up if the target disappears?
31
+ readonly #exception?: SDK.RemoteObject.RemoteObject;
32
+ #exceptionDetails?: Promise<ExceptionDetails|undefined>;
33
+
34
+ constructor(
35
+ status: Protocol.WebMCP.InvocationStatus, output: unknown|undefined, errorText: string|undefined,
36
+ exception: SDK.RemoteObject.RemoteObject|undefined) {
37
+ this.status = status;
38
+ this.errorText = errorText;
39
+ this.#exception = exception;
40
+ this.output = output;
41
+ }
42
+
43
+ get exceptionDetails(): Promise<ExceptionDetails|undefined>|undefined {
44
+ if (!this.#exceptionDetails) {
45
+ this.#exceptionDetails = this.#resolveExceptionDetails(this.#exception);
46
+ }
47
+ return this.#exceptionDetails;
48
+ }
49
+
50
+ async #resolveExceptionDetails(errorObj: SDK.RemoteObject.RemoteObject|undefined):
51
+ Promise<ExceptionDetails|undefined> {
52
+ if (!errorObj) {
53
+ return undefined;
54
+ }
55
+ const error = SDK.RemoteObject.RemoteError.objectAsError(errorObj);
56
+ const [details, cause] = await Promise.all([error.exceptionDetails(), error.cause()]);
57
+ const description = error.errorStack;
58
+
59
+ const frames =
60
+ StackTrace.ErrorStackParser.parseSourcePositionsFromErrorStack(errorObj.runtimeModel(), error.errorStack) || [];
61
+ if (details?.stackTrace) {
62
+ StackTrace.ErrorStackParser.augmentErrorStackWithScriptIds(frames, details.stackTrace);
63
+ }
64
+
65
+ if (cause?.subtype === 'error') {
66
+ return {error: errorObj, description, frames, cause: await this.#resolveExceptionDetails(cause)};
67
+ }
68
+
69
+ if (cause?.type === 'string') {
70
+ return {
71
+ error: errorObj,
72
+ description,
73
+ frames,
74
+ cause: {
75
+ error: cause,
76
+ description: cause.value as string,
77
+ frames: [],
78
+ }
79
+ };
80
+ }
81
+
82
+ return {error: errorObj, description, frames};
83
+ }
84
+ }
85
+
19
86
  export interface Call {
20
87
  invocationId: string;
21
88
  tool: Tool;
22
89
  input: string;
23
- result?: {
24
- status: Protocol.WebMCP.InvocationStatus,
25
- output?: unknown,
26
- errorText?: string,
27
- exception?: Protocol.Runtime.RemoteObject,
28
- };
90
+ result?: Result;
29
91
  }
30
92
 
31
93
  export class Tool {
@@ -160,11 +222,7 @@ export class WebMCPModel extends SDK.SDKModel.SDKModel<EventTypes> implements Pr
160
222
  if (!tool) {
161
223
  return;
162
224
  }
163
- const call: Call = {
164
- invocationId: params.invocationId,
165
- input: params.input,
166
- tool,
167
- };
225
+ const call: Call = {tool, input: params.input, invocationId: params.invocationId};
168
226
  this.#calls.set(params.invocationId, call);
169
227
  this.dispatchEventToListeners(Events.TOOL_INVOKED, call);
170
228
  }
@@ -174,12 +232,9 @@ export class WebMCPModel extends SDK.SDKModel.SDKModel<EventTypes> implements Pr
174
232
  if (!call) {
175
233
  return;
176
234
  }
177
- call.result = {
178
- status: params.status,
179
- output: params.output,
180
- errorText: params.errorText,
181
- exception: params.exception,
182
- };
235
+ const exception =
236
+ params.exception && this.target().model(SDK.RuntimeModel.RuntimeModel)?.createRemoteObject(params.exception);
237
+ call.result = new Result(params.status, params.output, params.errorText, exception);
183
238
  this.dispatchEventToListeners(Events.TOOL_RESPONDED, call);
184
239
  }
185
240
  }
@@ -306,9 +306,8 @@ async function getEmptyStateSuggestions(conversation?: AiAssistanceModel.AiConve
306
306
  ];
307
307
  case AiAssistanceModel.AiHistoryStorage.ConversationType.ACCESSIBILITY:
308
308
  return [
309
- {title: 'What are the accessibility issues on this page?', jslogContext: 'accessibility-default'},
310
- {title: 'How can I fix these accessibility issues?', jslogContext: 'accessibility-default'},
311
- {title: 'What does this Lighthouse report say about accessibility?', jslogContext: 'accessibility-default'},
309
+ {title: 'How can I fix accessibility issues on my page?', jslogContext: 'accessibility-default'},
310
+ {title: 'What accessibility issues exist on my page?', jslogContext: 'accessibility-default'},
312
311
  ];
313
312
  case AiAssistanceModel.AiHistoryStorage.ConversationType.NETWORK:
314
313
 
@@ -157,9 +157,13 @@ const UIStringsNotTranslate = {
157
157
  */
158
158
  completed: 'Completed',
159
159
  /**
160
- * @description Aria label for the cancel icon to be read by screen reader
160
+ * @description Aria label for the spinner to be read by screen reader when a step is in progress.
161
161
  */
162
- canceled: 'Canceled',
162
+ inProgress: 'In progress',
163
+ /**
164
+ * @description Aria label for the aborted icon to be read by screen reader
165
+ */
166
+ aborted: 'Aborted',
163
167
  /**
164
168
  * @description Alt text for the image input (displayed in the chat messages) that has been sent to the model.
165
169
  */
@@ -204,6 +208,14 @@ const UIStringsNotTranslate = {
204
208
  * @description Title for the bottom up thread activity widget.
205
209
  */
206
210
  bottomUpTree: 'Bottom-up thread activity',
211
+ /**
212
+ * @description Accessilility label for the button that shows the walkthrough when there are no widgets in the walkthrough.
213
+ */
214
+ showThinking: 'Show thinking',
215
+ /**
216
+ * @description Accessilility label for the button that hides the walkthrough when there are no widgets in the walkthrough.
217
+ */
218
+ hideThinking: 'Hide thinking',
207
219
  } as const;
208
220
 
209
221
  export interface Step {
@@ -463,7 +475,7 @@ function renderTitle(step: Step): Lit.LitTemplate {
463
475
  html`<span class="paused">${lockedString(UIStringsNotTranslate.paused)}: </span>` :
464
476
  Lit.nothing;
465
477
 
466
- return html`<span class="title">${paused}${titleForStep(step)}</span>`;
478
+ return html`<span class="title" aria-label=${titleForStep(step)}>${paused}${titleForStep(step)}</span>`;
467
479
  }
468
480
 
469
481
  function renderStepCode(step: Step): Lit.LitTemplate {
@@ -571,6 +583,14 @@ function renderWalkthroughSidebarButton(
571
583
  // We only apply the widget styling when loading is complete
572
584
  'has-widgets': hasOneStepWithWidget && !input.isLoading,
573
585
  });
586
+
587
+ let accessibleLabel = title;
588
+ // If the agent is still thinking we want the accessibility label to include the current step title followed by Show/Hide thinking.
589
+ if (input.isLoading) {
590
+ const suffix = isExpanded ? UIStringsNotTranslate.hideThinking : UIStringsNotTranslate.showThinking;
591
+ accessibleLabel = `${titleForStep(lastStep)} ${i18n.i18n.lockedString(suffix)}`;
592
+ }
593
+
574
594
  // clang-format off
575
595
  return html`
576
596
  <div class=${toggleContainerClasses}>
@@ -581,6 +601,7 @@ function renderWalkthroughSidebarButton(
581
601
  .variant=${variant}
582
602
  .size=${Buttons.Button.Size.SMALL}
583
603
  .title=${lastStep.isLoading ? titleForStep(lastStep) : title}
604
+ .accessibleLabel=${accessibleLabel}
584
605
  .jslogContext=${walkthrough.isExpanded ? 'ai-hide-walkthrough-sidebar' : 'ai-show-walkthrough-sidebar'}
585
606
  data-show-walkthrough
586
607
  @click=${() => {
@@ -670,7 +691,7 @@ function renderStepBadge({step, isLoading, isLast}: {
670
691
  isLast: boolean,
671
692
  }): Lit.LitTemplate {
672
693
  if (isLoading && isLast && !step.requestApproval) {
673
- return html`<devtools-spinner></devtools-spinner>`;
694
+ return html`<devtools-spinner aria-label=${lockedString(UIStringsNotTranslate.inProgress)}></devtools-spinner>`;
674
695
  }
675
696
 
676
697
  let iconName = 'checkmark';
@@ -678,10 +699,10 @@ function renderStepBadge({step, isLoading, isLast}: {
678
699
  let role: 'button'|undefined = 'button';
679
700
  if (isLast && step.requestApproval) {
680
701
  role = undefined;
681
- ariaLabel = undefined;
702
+ ariaLabel = lockedString(UIStringsNotTranslate.paused);
682
703
  iconName = 'pause-circle';
683
704
  } else if (step.canceled) {
684
- ariaLabel = lockedString(UIStringsNotTranslate.canceled);
705
+ ariaLabel = lockedString(UIStringsNotTranslate.aborted);
685
706
  iconName = 'cross';
686
707
  }
687
708
 
@@ -925,6 +946,7 @@ function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTempla
925
946
  const revealButton = html`
926
947
  <devtools-button class="widget-reveal-button"
927
948
  .variant=${Buttons.Button.Variant.TEXT}
949
+ .accessibleLabel=${lockedString(UIStringsNotTranslate.reveal)}
928
950
  @click=${onReveal}
929
951
  >
930
952
  ${response.customRevealTitle ?? lockedString(UIStringsNotTranslate.reveal)}
@@ -937,7 +959,7 @@ function renderWidgetResponse(response: WidgetMakerResponse|null): Lit.LitTempla
937
959
  <div class=${classes}>
938
960
  ${response.title ? html`
939
961
  <div class="widget-header">
940
- <div class="widget-name">${response.title}</div>
962
+ <h3 class="widget-name">${response.title}</h3>
941
963
  <div class="widget-reveal-container">
942
964
  ${revealButton}
943
965
  </div>
@@ -1242,6 +1264,7 @@ function renderActions(input: ChatMessageViewInput, output: ViewOutput): Lit.Lit
1242
1264
  .jslogContext=${'ai-export-for-agents'}
1243
1265
  .variant=${Buttons.Button.Variant.OUTLINED}
1244
1266
  .iconName=${'copy'}
1267
+ aria-label=${lockedString(UIStringsNotTranslate.exportForAgents)}
1245
1268
  @click=${input.onExportClick}
1246
1269
  >${lockedString(UIStringsNotTranslate.exportForAgents)}</devtools-button>
1247
1270
  ${input.suggestions ? html`<div class="vertical-separator"></div>` : Lit.nothing}
@@ -87,17 +87,18 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
87
87
  <style>${styles}</style>
88
88
  <div class="export-for-agents-dialog">
89
89
  <header>
90
- <h2 tabindex="-1">
90
+ <h1 id="export-for-agents-dialog-title" tabindex="-1">
91
91
  ${i18nString(UIStrings.exportForAgents)}
92
- </h2>
92
+ </h1>
93
93
  </header>
94
- <div class="state-selection">
94
+ <div class="state-selection" role="radiogroup" aria-labelledby="export-for-agents-dialog-title">
95
95
  <label>
96
96
  <input
97
97
  type="radio"
98
98
  value="prompt"
99
99
  name="export-state"
100
100
  .checked=${isPrompt}
101
+ aria-label=${i18nString(UIStrings.asPrompt)}
101
102
  @change=${() => input.onStateChange(StateType.PROMPT)}
102
103
  >
103
104
  ${i18nString(UIStrings.asPrompt)}
@@ -108,6 +109,7 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
108
109
  value="conversation"
109
110
  name="export-state"
110
111
  .checked=${!isPrompt}
112
+ aria-label=${i18nString(UIStrings.asMarkdown)}
111
113
  @change=${() => input.onStateChange(StateType.CONVERSATION)}
112
114
  >
113
115
  ${i18nString(UIStrings.asMarkdown)}
@@ -130,6 +132,7 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
130
132
  .jslogContext=${input.jslogContext}
131
133
  .variant=${Buttons.Button.Variant.PRIMARY}
132
134
  .disabled=${isPrompt && input.state.isPromptLoading}
135
+ .accessibleLabel=${buttonText}
133
136
  >
134
137
  ${buttonText}
135
138
  </devtools-button>
@@ -191,9 +194,10 @@ export class ExportForAgentsDialog extends UI.Widget.VBox {
191
194
  onButtonClick = (event: Event): void => {
192
195
  event.preventDefault();
193
196
  Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText(this.#state.promptText);
194
- Snackbars.Snackbar.Snackbar.show({
197
+ const snackbar = Snackbars.Snackbar.Snackbar.show({
195
198
  message: i18nString(UIStrings.copiedToClipboard),
196
199
  });
200
+ snackbar.setAttribute('aria-label', i18nString(UIStrings.copiedToClipboard));
197
201
  this.#dialog.hide();
198
202
  };
199
203
  break;
@@ -45,6 +45,10 @@ const UIStrings = {
45
45
  * @description Title for the button that hides the walkthrough when there are widgets in the walkthrough.
46
46
  */
47
47
  hideAgentWalkthrough: 'Hide agent walkthrough',
48
+ /**
49
+ * @description Aria label for the spinner to be read by screen reader when a step is in progress.
50
+ */
51
+ inProgress: 'In progress',
48
52
  } as const;
49
53
  const str_ = i18n.i18n.registerUIStrings('panels/ai_assistance/components/WalkthroughView.ts', UIStrings);
50
54
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -118,7 +122,7 @@ function renderInlineWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate,
118
122
  <div class="inline-wrapper" ?data-open=${input.isExpanded}>
119
123
  <span class="inline-icon">
120
124
  ${input.isLoading ?
121
- html`<devtools-spinner></devtools-spinner>` :
125
+ html`<devtools-spinner aria-label=${lockedString(UIStrings.inProgress)}></devtools-spinner>` :
122
126
  html`<devtools-icon name=${icon}></devtools-icon>`
123
127
  }
124
128
  </span>
@@ -149,7 +153,7 @@ function renderSidebarWalkthrough(input: ViewInput, stepsOutput: Lit.LitTemplate
149
153
  return html`
150
154
  <div class="walkthrough-view">
151
155
  <div class="walkthrough-header">
152
- <div class="walkthrough-title">${i18nString(UIStrings.title)}</div>
156
+ <h2 class="walkthrough-title">${i18nString(UIStrings.title)}</h2>
153
157
  <devtools-button
154
158
  .data=${{
155
159
  variant: Buttons.Button.Variant.TOOLBAR,
@@ -21,7 +21,7 @@
21
21
  .export-for-agents-dialog header {
22
22
  margin-bottom: var(--sys-size-6);
23
23
 
24
- h2 {
24
+ h1 {
25
25
  font: var(--sys-typescale-headline5);
26
26
  margin: 0;
27
27
  color: var(--sys-color-on-surface);
@@ -118,21 +118,9 @@
118
118
  &[open] {
119
119
  border-radius: var(--sys-size-5);
120
120
  width: auto;
121
- background-color: var(--sys-color-surface1);
121
+ background-color: var(--sys-color-surface2);
122
122
  margin-left: calc(var(--sys-size-6) / 2);
123
123
  flex-grow: 1;
124
-
125
- > summary {
126
- border-radius: var(--sys-shape-corner-medium-small);
127
- border-bottom-right-radius: 0;
128
- border-bottom-left-radius: 0;
129
- background: var(--sys-color-surface5);
130
- color: var(--sys-color-on-surface);
131
-
132
- &[data-has-widgets] {
133
- margin-left: 0;
134
- }
135
- }
136
124
  }
137
125
  }
138
126
 
@@ -177,6 +165,23 @@
177
165
  }
178
166
  }
179
167
 
168
+ .walkthrough-inline[open] > summary {
169
+ border-radius: var(--sys-shape-corner-medium-small);
170
+ border-bottom-right-radius: 0;
171
+ border-bottom-left-radius: 0;
172
+ background: var(--sys-color-surface5);
173
+ color: var(--sys-color-on-surface);
174
+
175
+ &[data-has-widgets] {
176
+ margin-left: 0;
177
+ }
178
+
179
+ > devtools-icon[name='chevron-right'] {
180
+ transform: rotate(270deg);
181
+ }
182
+
183
+ }
184
+
180
185
  .walkthrough-inline > summary::-webkit-details-marker {
181
186
  display: none;
182
187
  }
@@ -201,8 +206,4 @@
201
206
  .walkthrough-inline .step {
202
207
  background-color: var(--sys-color-surface5);
203
208
  }
204
-
205
- .walkthrough-inline[open] > summary > devtools-icon[name='chevron-right'] {
206
- transform: rotate(270deg);
207
- }
208
209
  }