chrome-devtools-frontend 1.0.1624409 → 1.0.1624583

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 (74) hide show
  1. package/.agents/skills/foundation-test-migration/SKILL.md +1 -1
  2. package/front_end/core/host/AidaClient.ts +6 -5
  3. package/front_end/core/host/AidaClientTypes.ts +3 -1
  4. package/front_end/core/host/DispatchHttpRequestClient.ts +6 -3
  5. package/front_end/core/platform/api/HostRuntime.ts +3 -0
  6. package/front_end/core/platform/browser/HostRuntime.ts +10 -0
  7. package/front_end/core/platform/node/HostRuntime.ts +10 -0
  8. package/front_end/core/root/Runtime.ts +6 -5
  9. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +2 -3
  10. package/front_end/models/ai_code_generation/AiCodeGeneration.ts +2 -3
  11. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +8 -4
  12. package/front_end/panels/ai_assistance/components/ChatMessage.ts +7 -5
  13. package/front_end/panels/ai_assistance/components/ChatView.ts +2 -2
  14. package/front_end/panels/elements/StylePropertyTreeElement.ts +2 -2
  15. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +6 -1
  16. package/front_end/panels/elements/StylesSidebarPane.ts +12 -1
  17. package/front_end/panels/elements/stylePropertiesTreeOutline.css +5 -1
  18. package/front_end/third_party/lighthouse/README.chromium +2 -2
  19. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +5176 -5104
  20. package/front_end/third_party/lighthouse/locales/ar-XB.json +72 -36
  21. package/front_end/third_party/lighthouse/locales/ar.json +72 -36
  22. package/front_end/third_party/lighthouse/locales/bg.json +72 -36
  23. package/front_end/third_party/lighthouse/locales/ca.json +72 -36
  24. package/front_end/third_party/lighthouse/locales/cs.json +72 -36
  25. package/front_end/third_party/lighthouse/locales/da.json +74 -38
  26. package/front_end/third_party/lighthouse/locales/de.json +72 -36
  27. package/front_end/third_party/lighthouse/locales/el.json +73 -37
  28. package/front_end/third_party/lighthouse/locales/en-GB.json +74 -38
  29. package/front_end/third_party/lighthouse/locales/en-US.json +245 -17
  30. package/front_end/third_party/lighthouse/locales/en-XL.json +245 -17
  31. package/front_end/third_party/lighthouse/locales/es-419.json +72 -36
  32. package/front_end/third_party/lighthouse/locales/es.json +73 -37
  33. package/front_end/third_party/lighthouse/locales/fi.json +72 -36
  34. package/front_end/third_party/lighthouse/locales/fil.json +74 -38
  35. package/front_end/third_party/lighthouse/locales/fr.json +162 -126
  36. package/front_end/third_party/lighthouse/locales/he.json +74 -38
  37. package/front_end/third_party/lighthouse/locales/hi.json +73 -37
  38. package/front_end/third_party/lighthouse/locales/hr.json +72 -36
  39. package/front_end/third_party/lighthouse/locales/hu.json +73 -37
  40. package/front_end/third_party/lighthouse/locales/id.json +74 -38
  41. package/front_end/third_party/lighthouse/locales/it.json +72 -36
  42. package/front_end/third_party/lighthouse/locales/ja.json +72 -36
  43. package/front_end/third_party/lighthouse/locales/ko.json +72 -36
  44. package/front_end/third_party/lighthouse/locales/lt.json +72 -36
  45. package/front_end/third_party/lighthouse/locales/lv.json +72 -36
  46. package/front_end/third_party/lighthouse/locales/nl.json +73 -37
  47. package/front_end/third_party/lighthouse/locales/no.json +72 -36
  48. package/front_end/third_party/lighthouse/locales/pl.json +72 -36
  49. package/front_end/third_party/lighthouse/locales/pt-PT.json +72 -36
  50. package/front_end/third_party/lighthouse/locales/pt.json +74 -38
  51. package/front_end/third_party/lighthouse/locales/ro.json +72 -36
  52. package/front_end/third_party/lighthouse/locales/ru.json +72 -36
  53. package/front_end/third_party/lighthouse/locales/sk.json +72 -36
  54. package/front_end/third_party/lighthouse/locales/sl.json +72 -36
  55. package/front_end/third_party/lighthouse/locales/sr-Latn.json +73 -37
  56. package/front_end/third_party/lighthouse/locales/sr.json +73 -37
  57. package/front_end/third_party/lighthouse/locales/sv.json +75 -39
  58. package/front_end/third_party/lighthouse/locales/ta.json +73 -37
  59. package/front_end/third_party/lighthouse/locales/te.json +72 -36
  60. package/front_end/third_party/lighthouse/locales/th.json +73 -37
  61. package/front_end/third_party/lighthouse/locales/tr.json +72 -36
  62. package/front_end/third_party/lighthouse/locales/uk.json +72 -36
  63. package/front_end/third_party/lighthouse/locales/vi.json +74 -38
  64. package/front_end/third_party/lighthouse/locales/zh-HK.json +72 -36
  65. package/front_end/third_party/lighthouse/locales/zh-TW.json +74 -38
  66. package/front_end/third_party/lighthouse/locales/zh.json +75 -39
  67. package/front_end/third_party/lighthouse/report/bundle.js +2 -1
  68. package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +1 -1
  69. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +6 -2
  70. package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +4 -1
  71. package/front_end/ui/legacy/SuggestBox.ts +1 -0
  72. package/front_end/ui/legacy/TextPrompt.ts +2 -1
  73. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  74. package/package.json +9 -8
@@ -126,7 +126,7 @@ sinon.assert.calledOnceWithMatch(emulateSpy, {
126
126
  ### DOM Globals (`ReferenceError: FileReader is not defined`)
127
127
  Foundation tests run in Node.js where `window`, `FileReader`, and certain DOM string encodings don't exist.
128
128
  - **Fix**: Use isomorphic equivalents (e.g. `btoa()`, `Uint8Array`).
129
- - **Last Resort**: Skip the test with `it.skip()` if it explicitly tests browser quirks (e.g. legacy charset decoding).
129
+ - **Fix**: Abstract the APIs that are different between Node.js and Browser via `front_end/core/platform/api/HostRuntime.ts`.
130
130
 
131
131
  ### Initialization Order Lockups
132
132
  If a test times out (5000ms exceeded), it is usually an unhandled promise caused by a missing singleton. Double-check the constructor of the failing manager to see which `instance()` it listens to, and ensure that dependent singleton was created *first*.
@@ -3,6 +3,7 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as Common from '../common/common.js';
6
+ import * as Platform from '../platform/platform.js';
6
7
  import * as Root from '../root/root.js';
7
8
 
8
9
  import {
@@ -109,7 +110,7 @@ export class AidaClient {
109
110
  }
110
111
 
111
112
  static async checkAccessPreconditions(): Promise<AidaAccessPreconditions> {
112
- if (!navigator.onLine) {
113
+ if (!Platform.HostRuntime.HOST_RUNTIME.getOnLine()) {
113
114
  return AidaAccessPreconditions.NO_INTERNET;
114
115
  }
115
116
 
@@ -437,7 +438,7 @@ export function getClientFeatureName(feature: ClientFeature): string {
437
438
  let hostConfigTrackerInstance: HostConfigTracker|undefined;
438
439
 
439
440
  export class HostConfigTracker extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
440
- #pollTimer?: number;
441
+ #pollTimer?: ReturnType<typeof setTimeout>;
441
442
  #aidaAvailability?: AidaAccessPreconditions;
442
443
 
443
444
  private constructor() {
@@ -456,7 +457,7 @@ export class HostConfigTracker extends Common.ObjectWrapper.ObjectWrapper<EventT
456
457
  const isFirst = !this.hasEventListeners(eventType);
457
458
  const eventDescriptor = super.addEventListener(eventType, listener);
458
459
  if (isFirst) {
459
- window.clearTimeout(this.#pollTimer);
460
+ clearTimeout(this.#pollTimer);
460
461
  void this.pollAidaAvailability();
461
462
  }
462
463
  return eventDescriptor;
@@ -466,12 +467,12 @@ export class HostConfigTracker extends Common.ObjectWrapper.ObjectWrapper<EventT
466
467
  void {
467
468
  super.removeEventListener(eventType, listener);
468
469
  if (!this.hasEventListeners(eventType)) {
469
- window.clearTimeout(this.#pollTimer);
470
+ clearTimeout(this.#pollTimer);
470
471
  }
471
472
  }
472
473
 
473
474
  async pollAidaAvailability(): Promise<void> {
474
- this.#pollTimer = window.setTimeout(() => this.pollAidaAvailability(), 2000);
475
+ this.#pollTimer = setTimeout(() => this.pollAidaAvailability(), 2000);
475
476
  const currentAidaAvailability = await AidaClient.checkAccessPreconditions();
476
477
  if (currentAidaAvailability !== this.#aidaAvailability) {
477
478
  this.#aidaAvailability = currentAidaAvailability;
@@ -2,6 +2,8 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
+ import * as Platform from '../platform/platform.js';
6
+
5
7
  export enum Role {
6
8
  /** Provide this role when giving a function call response */
7
9
  ROLE_UNSPECIFIED = 0,
@@ -466,7 +468,7 @@ export interface AidaChunkResponse {
466
468
  }
467
469
 
468
470
  export function debugLog(...log: unknown[]): void {
469
- if (!Boolean(localStorage.getItem('debugAiServicesEnabled'))) {
471
+ if (!Boolean(Platform.HostRuntime.HOST_RUNTIME.getLocalStorage()?.getItem('debugAiServicesEnabled'))) {
470
472
  return;
471
473
  }
472
474
 
@@ -2,6 +2,8 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
+ import * as Platform from '../platform/platform.js';
6
+
5
7
  import {InspectorFrontendHostInstance} from './InspectorFrontendHost.js';
6
8
  import type {DispatchHttpRequestRequest, DispatchHttpRequestResult} from './InspectorFrontendHostAPI.js';
7
9
 
@@ -56,7 +58,7 @@ export async function makeHttpRequest<R>(
56
58
  }
57
59
 
58
60
  function isDebugMode(): boolean {
59
- return Boolean(localStorage.getItem('debugDispatchHttpRequestEnabled'));
61
+ return Boolean(Platform.HostRuntime.HOST_RUNTIME.getLocalStorage()?.getItem('debugDispatchHttpRequestEnabled'));
60
62
  }
61
63
 
62
64
  function debugLog(...log: unknown[]): void {
@@ -69,10 +71,11 @@ function debugLog(...log: unknown[]): void {
69
71
  }
70
72
 
71
73
  function setDebugDispatchHttpRequestEnabled(enabled: boolean): void {
74
+ const localStorage = Platform.HostRuntime.HOST_RUNTIME.getLocalStorage();
72
75
  if (enabled) {
73
- localStorage.setItem('debugDispatchHttpRequestEnabled', 'true');
76
+ localStorage?.setItem('debugDispatchHttpRequestEnabled', 'true');
74
77
  } else {
75
- localStorage.removeItem('debugDispatchHttpRequestEnabled');
78
+ localStorage?.removeItem('debugDispatchHttpRequestEnabled');
76
79
  }
77
80
  }
78
81
 
@@ -9,6 +9,9 @@
9
9
  export interface HostRuntime {
10
10
  createWorker(url: string): Worker;
11
11
  workerScope: WorkerScope;
12
+ getOnLine(): boolean;
13
+ getUserAgent(): string;
14
+ getLocalStorage(): Storage|undefined;
12
15
  }
13
16
 
14
17
  /**
@@ -74,4 +74,14 @@ export const HOST_RUNTIME: Api.HostRuntime.HostRuntime = {
74
74
  return new WebWorker(url);
75
75
  },
76
76
  workerScope: new WebWorkerScope(),
77
+ getOnLine(): boolean {
78
+ return navigator.onLine;
79
+ },
80
+ getUserAgent(): string {
81
+ return navigator.userAgent;
82
+ },
83
+ getLocalStorage(): Storage |
84
+ undefined {
85
+ return 'localStorage' in globalThis ? globalThis.localStorage : undefined;
86
+ },
77
87
  };
@@ -82,4 +82,14 @@ export const HOST_RUNTIME: Api.HostRuntime.HostRuntime = {
82
82
  return new NodeWorker(url);
83
83
  },
84
84
  workerScope: new NodeWorkerScope(),
85
+ getOnLine(): boolean {
86
+ return true;
87
+ },
88
+ getUserAgent(): string {
89
+ return 'Node.js';
90
+ },
91
+ getLocalStorage(): Storage |
92
+ undefined {
93
+ return undefined;
94
+ },
85
95
  };
@@ -61,7 +61,8 @@ export function isNodeEntry(pathname: string): boolean {
61
61
 
62
62
  export const getChromeVersion = (): string => {
63
63
  const chromeRegex = /(?:^|\W)(?:Chrome|HeadlessChrome)\/(\S+)/;
64
- const chromeMatch = globalObject.navigator?.userAgent?.match(chromeRegex);
64
+ const userAgent = Platform.HostRuntime.HOST_RUNTIME.getUserAgent();
65
+ const chromeMatch = userAgent.match(chromeRegex);
65
66
  if (chromeMatch && chromeMatch.length > 1) {
66
67
  return chromeMatch[1];
67
68
  }
@@ -317,12 +318,12 @@ class ExperimentStorage {
317
318
 
318
319
  constructor() {
319
320
  try {
320
- const storedExperiments = globalObject.localStorage?.getItem('experiments');
321
+ const storedExperiments = Platform.HostRuntime.HOST_RUNTIME.getLocalStorage()?.getItem('experiments');
321
322
  if (storedExperiments) {
322
323
  this.#experiments = JSON.parse(storedExperiments);
323
324
  }
324
- } catch {
325
- console.error('Failed to parse localStorage[\'experiments\']');
325
+ } catch (err) {
326
+ console.error('Failed to parse localStorage[\'experiments\']: ' + err.message);
326
327
  }
327
328
  }
328
329
 
@@ -351,7 +352,7 @@ class ExperimentStorage {
351
352
  }
352
353
 
353
354
  #syncToLocalStorage(): void {
354
- globalObject.localStorage?.setItem('experiments', JSON.stringify(this.#experiments));
355
+ Platform.HostRuntime.HOST_RUNTIME.getLocalStorage()?.setItem('experiments', JSON.stringify(this.#experiments));
355
356
  }
356
357
  }
357
358
 
@@ -14,7 +14,6 @@ import {debugLog} from './debug.js';
14
14
  interface AgentOptions {
15
15
  aidaClient: Host.AidaClient.AidaClient;
16
16
  serverSideLoggingEnabled?: boolean;
17
- confirmSideEffectForTest?: typeof Promise.withResolvers;
18
17
  }
19
18
 
20
19
  interface RequestOptions {
@@ -274,7 +273,7 @@ export class AiCodeCompletion {
274
273
 
275
274
  void this.#aidaClient.registerClientEvent({
276
275
  corresponding_aida_rpc_global_id: rpcGlobalId,
277
- disable_user_content_logging: true,
276
+ disable_user_content_logging: !(this.#serverSideLoggingEnabled ?? false),
278
277
  complete_code_client_event: {
279
278
  user_impression: {
280
279
  sample: {
@@ -296,7 +295,7 @@ export class AiCodeCompletion {
296
295
  registerUserAcceptance(rpcGlobalId: Host.AidaClient.RpcGlobalId, sampleId?: number): void {
297
296
  void this.#aidaClient.registerClientEvent({
298
297
  corresponding_aida_rpc_global_id: rpcGlobalId,
299
- disable_user_content_logging: true,
298
+ disable_user_content_logging: !(this.#serverSideLoggingEnabled ?? false),
300
299
  complete_code_client_event: {
301
300
  user_acceptance: {
302
301
  sample: {
@@ -39,7 +39,6 @@ The console has direct access to the inspected page's \`window\` and \`document\
39
39
  interface Options {
40
40
  aidaClient: Host.AidaClient.AidaClient;
41
41
  serverSideLoggingEnabled?: boolean;
42
- confirmSideEffectForTest?: typeof Promise.withResolvers;
43
42
  }
44
43
 
45
44
  interface RequestOptions {
@@ -124,7 +123,7 @@ export class AiCodeGeneration {
124
123
 
125
124
  void this.#aidaClient.registerClientEvent({
126
125
  corresponding_aida_rpc_global_id: rpcGlobalId,
127
- disable_user_content_logging: true,
126
+ disable_user_content_logging: !(this.#serverSideLoggingEnabled ?? false),
128
127
  generate_code_client_event: {
129
128
  user_impression: {
130
129
  sample: {
@@ -146,7 +145,7 @@ export class AiCodeGeneration {
146
145
  registerUserAcceptance(rpcGlobalId: Host.AidaClient.RpcGlobalId, sampleId?: number): void {
147
146
  void this.#aidaClient.registerClientEvent({
148
147
  corresponding_aida_rpc_global_id: rpcGlobalId,
149
- disable_user_content_logging: true,
148
+ disable_user_content_logging: !(this.#serverSideLoggingEnabled ?? false),
150
149
  generate_code_client_event: {
151
150
  user_acceptance: {
152
151
  sample: {
@@ -531,7 +531,7 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
531
531
  let walkthroughIsForLastMessage = false;
532
532
  if(input.state === ViewState.CHAT_VIEW) {
533
533
  const lastMessage = input.props.messages.at(-1);
534
- if(lastMessage && input.props.walkthrough.activeSidebarMessage === lastMessage) {
534
+ if(lastMessage && input.props.walkthrough.activeSidebarMessage?.id === lastMessage.id) {
535
535
  walkthroughIsForLastMessage = true;
536
536
  }
537
537
  }
@@ -879,7 +879,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
879
879
  }
880
880
 
881
881
  #openWalkthrough(message: ModelChatMessage): void {
882
- if (!this.#walkthrough.inlineExpandedMessages.includes(message)) {
882
+ if (!this.#walkthrough.inlineExpandedMessages.some(m => m.id === message.id)) {
883
883
  this.#walkthrough.inlineExpandedMessages.push(message);
884
884
  }
885
885
  this.#walkthrough.activeSidebarMessage = message;
@@ -905,7 +905,8 @@ export class AiAssistancePanel extends UI.Panel.Panel {
905
905
  }
906
906
 
907
907
  // If we are closing a walkthrough, remove it from the list of expanded messages.
908
- this.#walkthrough.inlineExpandedMessages = this.#walkthrough.inlineExpandedMessages.filter(m => m !== message);
908
+ this.#walkthrough.inlineExpandedMessages =
909
+ this.#walkthrough.inlineExpandedMessages.filter(m => m.id !== message.id);
909
910
 
910
911
  if (this.#walkthrough.isInlined) {
911
912
  // In Narrow mode, the global expanded state tracks if at least one walkthrough is open.
@@ -913,7 +914,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
913
914
  // If the message we just closed was the active one, we pick a new active message
914
915
  // from the remaining open ones (if any). This ensures that if the user
915
916
  // re-opens the sidebar later, it shows the most recently opened walkthrough.
916
- if (this.#walkthrough.activeSidebarMessage === message) {
917
+ if (this.#walkthrough.activeSidebarMessage?.id === message.id) {
917
918
  this.#walkthrough.activeSidebarMessage = this.#walkthrough.inlineExpandedMessages.at(-1) ?? null;
918
919
  }
919
920
  } else {
@@ -1857,6 +1858,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1857
1858
  let systemMessage: ModelChatMessage = {
1858
1859
  entity: ChatMessageEntity.MODEL,
1859
1860
  parts: [],
1861
+ id: crypto.randomUUID(),
1860
1862
  };
1861
1863
  let step: Step = {isLoading: true};
1862
1864
 
@@ -1885,10 +1887,12 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1885
1887
  entity: ChatMessageEntity.USER,
1886
1888
  text: data.query,
1887
1889
  imageInput: data.imageInput,
1890
+ id: crypto.randomUUID(),
1888
1891
  });
1889
1892
  systemMessage = {
1890
1893
  entity: ChatMessageEntity.MODEL,
1891
1894
  parts: [],
1895
+ id: crypto.randomUUID(),
1892
1896
  };
1893
1897
  this.#messages.push(systemMessage);
1894
1898
  // If the walkthrough is currently expanded in the sidebar, we want to
@@ -435,6 +435,7 @@ export interface UserChatMessage {
435
435
  entity: ChatMessageEntity.USER;
436
436
  text: string;
437
437
  imageInput?: Host.AidaClient.Part;
438
+ id: string;
438
439
  }
439
440
 
440
441
  export interface ModelChatMessage {
@@ -442,6 +443,7 @@ export interface ModelChatMessage {
442
443
  parts: ModelMessagePart[];
443
444
  error?: AiAssistanceModel.AiAgent.ErrorType;
444
445
  rpcId?: Host.AidaClient.RpcGlobalId;
446
+ id: string;
445
447
  }
446
448
 
447
449
  export type Message = UserChatMessage|ModelChatMessage;
@@ -740,7 +742,7 @@ function renderWalkthroughSidebarButton(
740
742
  }
741
743
 
742
744
  const hasOneStepWithWidget = steps.some(step => step.widgets?.length);
743
- const isExpanded = walkthrough.isExpanded && input.message === input.walkthrough.activeSidebarMessage;
745
+ const isExpanded = walkthrough.isExpanded && input.message.id === input.walkthrough.activeSidebarMessage?.id;
744
746
  const title = isExpanded ? walkthroughCloseTitle({hasWidgets: hasOneStepWithWidget}) : walkthroughTitle({
745
747
  isLoading: input.isLoading,
746
748
  hasWidgets: hasOneStepWithWidget,
@@ -781,7 +783,7 @@ function renderWalkthroughSidebarButton(
781
783
  .jslogContext=${walkthrough.isExpanded ? 'ai-hide-walkthrough-sidebar' : 'ai-show-walkthrough-sidebar'}
782
784
  data-show-walkthrough
783
785
  @click=${() => {
784
- if(walkthrough.activeSidebarMessage === input.message && walkthrough.isExpanded) {
786
+ if(walkthrough.activeSidebarMessage?.id === input.message.id && walkthrough.isExpanded) {
785
787
  walkthrough.onToggle(false, message as ModelChatMessage);
786
788
  } else {
787
789
  // Can't just toggle the visibility here; we need to ensure we
@@ -816,8 +818,8 @@ function renderWalkthroughUI(input: ChatMessageViewInput, steps: Step[]): Lit.Li
816
818
  // open and it is specifically targeting this message. This is necessary
817
819
  // because the walkthrough state is shared across all messages in the chat.
818
820
  const isExpanded = input.walkthrough.isInlined ?
819
- input.walkthrough.inlineExpandedMessages.includes(input.message as ModelChatMessage) :
820
- (input.walkthrough.isExpanded && input.walkthrough.activeSidebarMessage === input.message);
821
+ input.walkthrough.inlineExpandedMessages.some(m => m.id === input.message.id) :
822
+ (input.walkthrough.isExpanded && input.walkthrough.activeSidebarMessage?.id === input.message.id);
821
823
 
822
824
  // clang-format off
823
825
  const walkthroughInline = input.walkthrough.isInlined ? html`
@@ -1775,7 +1777,7 @@ function renderActions(input: ChatMessageViewInput, output: ViewOutput): Lit.Lit
1775
1777
  }
1776
1778
 
1777
1779
  export class ChatMessage extends UI.Widget.Widget {
1778
- message: Message = {entity: ChatMessageEntity.USER, text: ''};
1780
+ message: Message = {entity: ChatMessageEntity.USER, text: '', id: ''};
1779
1781
  isLoading = false;
1780
1782
  isReadOnly = false;
1781
1783
  prompt = '';
@@ -155,7 +155,7 @@ const DEFAULT_VIEW: View = (input, output, target) => {
155
155
  <main @scroll=${input.handleScroll} ${ref(element => { output.mainElement = element as HTMLElement; } )}>
156
156
  ${input.messages.length > 0 ? html`
157
157
  <div class="messages-container" ${ref(input.handleMessageContainerRef)}>
158
- ${repeat(input.messages, (message, index) => {
158
+ ${repeat(input.messages, message => message.id, (message, index) => {
159
159
  const prevMessage = index > 0 ? input.messages[index - 1] : null;
160
160
  const prompt = (message.entity === ChatMessageEntity.MODEL && prevMessage?.entity === ChatMessageEntity.USER) ?
161
161
  prevMessage.text :
@@ -169,7 +169,7 @@ const DEFAULT_VIEW: View = (input, output, target) => {
169
169
  isLastMessage: index === input.messages.length - 1,
170
170
  isFirstMessage: index === 0,
171
171
  prompt,
172
- shouldShowCSSChangeSummary: message === cssChangeSummaryMessage,
172
+ shouldShowCSSChangeSummary: message.id === cssChangeSummaryMessage?.id,
173
173
  onSuggestionClick: input.handleSuggestionClick,
174
174
  onFeedbackSubmit: input.onFeedbackSubmit,
175
175
  onCopyResponseClick: input.onCopyResponseClick,
@@ -3637,12 +3637,12 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
3637
3637
  }
3638
3638
  const isEditingName = UI.UIUtils.isBeingEdited(this.nameElement);
3639
3639
  if (isEditingName) {
3640
- this.prompt.applySuggestion({text: activeAiSuggestion.name}, true);
3640
+ this.prompt.applySuggestion({text: activeAiSuggestion.name, disableAcceptSuggestionOnStopCharacters: true}, true);
3641
3641
  this.#showGhostTextInValue(activeAiSuggestion.value);
3642
3642
  } else {
3643
3643
  // Only has ghost text for one field - name part or value part
3644
3644
  const currentSuggestedText = isEditingName ? activeAiSuggestion.name : activeAiSuggestion.value;
3645
- this.prompt.applySuggestion({text: currentSuggestedText}, true);
3645
+ this.prompt.applySuggestion({text: currentSuggestedText, disableAcceptSuggestionOnStopCharacters: true}, true);
3646
3646
  }
3647
3647
  }
3648
3648
 
@@ -5,6 +5,7 @@
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as Host from '../../core/host/host.js';
7
7
  import * as i18n from '../../core/i18n/i18n.js';
8
+ import * as Root from '../../core/root/root.js';
8
9
  import type * as SDK from '../../core/sdk/sdk.js';
9
10
  import * as AiCodeCompletion from '../../models/ai_code_completion/ai_code_completion.js';
10
11
  import * as TextUtils from '../../models/text_utils/text_utils.js';
@@ -61,7 +62,11 @@ export class StylesAiCodeCompletionProvider {
61
62
  stopSequences.push(...this.#aiCodeCompletionConfig.completionContext.stopSequences);
62
63
  }
63
64
  this.#aiCodeCompletion = new AiCodeCompletion.AiCodeCompletion.AiCodeCompletion(
64
- {aidaClient: this.#aidaClient}, this.#aiCodeCompletionConfig.panel, undefined, stopSequences);
65
+ {
66
+ aidaClient: this.#aidaClient,
67
+ serverSideLoggingEnabled: !Root.Runtime.hostConfig.aidaAvailability?.disallowLogging
68
+ },
69
+ this.#aiCodeCompletionConfig.panel, undefined, stopSequences);
65
70
  this.#aiCodeCompletionConfig.onFeatureEnabled();
66
71
  }
67
72
 
@@ -135,10 +135,16 @@ const UIStrings = {
135
135
  * @description Tooltip text for the link in the sidebar pane layer separators that reveals the layer in the layer tree view.
136
136
  */
137
137
  clickToRevealLayer: 'Click to reveal layer in layer tree',
138
+ /**
139
+ * @description Text to announce that the AI suggestion was accepted.
140
+ * @example {color: blue;} PH1
141
+ */
142
+ aiSuggestionAccepted: '{PH1} Suggestion accepted.',
138
143
  } as const;
139
144
 
140
145
  const str_ = i18n.i18n.registerUIStrings('panels/elements/StylesSidebarPane.ts', UIStrings);
141
146
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
147
+ const lockedString = i18n.i18n.lockedString;
142
148
 
143
149
  // Number of ms elapsed with no keypresses to determine is the input is finished, to announce results
144
150
  const FILTER_IDLE_PERIOD = 500;
@@ -2289,6 +2295,7 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
2289
2295
  if (args.rpcGlobalId) {
2290
2296
  args.onImpression(args.rpcGlobalId, latency, args.sampleId);
2291
2297
  }
2298
+ UI.ARIAUtils.LiveAnnouncer.status(lockedString(styleText));
2292
2299
  }
2293
2300
 
2294
2301
  #getAiSuggestedProperties(suggestionText: string): ActiveAiSuggestionProperty[] {
@@ -2369,7 +2376,7 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
2369
2376
  // Explicitly set the query range as it is cleared during `acceptAutoComplete`
2370
2377
  this.queryRange = new TextUtils.TextRange.TextRange(0, 0, 0, textAfterAccept.length);
2371
2378
  // Re-apply the ghost text for the remainder
2372
- this.applySuggestion({text: suggestionForCurrentPrompt}, true);
2379
+ this.applySuggestion({text: suggestionForCurrentPrompt, disableAcceptSuggestionOnStopCharacters: true}, true);
2373
2380
  }
2374
2381
  return true;
2375
2382
  }
@@ -2384,12 +2391,16 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
2384
2391
  }
2385
2392
 
2386
2393
  async commitAiSuggestion(): Promise<void> {
2394
+ const suggestionText = this.treeElement.section().activeAiSuggestion?.text;
2387
2395
  await this.treeElement.section().commitActiveAiSuggestion();
2388
2396
  if (this.activeAiSuggestionInfo) {
2389
2397
  this.aiCodeCompletionProvider?.onSuggestionAccepted(
2390
2398
  this.activeAiSuggestionInfo.citations, this.activeAiSuggestionInfo.rpcGlobalId,
2391
2399
  this.activeAiSuggestionInfo.sampleId);
2392
2400
  }
2401
+ if (suggestionText) {
2402
+ UI.ARIAUtils.LiveAnnouncer.status(i18nString(UIStrings.aiSuggestionAccepted, {PH1: suggestionText}));
2403
+ }
2393
2404
  // Clear state and return
2394
2405
  this.setAiAutoCompletion(null);
2395
2406
  }
@@ -89,7 +89,11 @@
89
89
  }
90
90
 
91
91
  &.ghost-row, .ghost-value-prediction {
92
- opacity: 50%;
92
+ .webkit-css-property {
93
+ opacity: 50%;
94
+ }
95
+
96
+ color: var(--sys-color-token-subtle);
93
97
  font-style: italic;
94
98
  pointer-events: none;
95
99
  }
@@ -1,7 +1,7 @@
1
1
  Name: Lighthouse
2
2
  Short Name: lighthouse
3
- Version: 13.1.0
4
- Revision: 0248afea9c9443c9fddd73e705256e38e8c6e042
3
+ Version: 13.2.0
4
+ Revision: a160d341f363acf2d99314214a6e0de7d0fead1f
5
5
  Update Mechanism: Manual
6
6
  URL: https://github.com/GoogleChrome/lighthouse
7
7
  License: Apache-2.0