chrome-devtools-frontend 1.0.1593518 → 1.0.1593959

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 (50) hide show
  1. package/docs/contributing/settings-experiments-features.md +75 -100
  2. package/front_end/core/host/UserMetrics.ts +1 -2
  3. package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
  4. package/front_end/core/root/ExperimentNames.ts +0 -1
  5. package/front_end/core/sdk/EmulationModel.ts +41 -1
  6. package/front_end/entrypoints/main/MainImpl.ts +0 -6
  7. package/front_end/generated/InspectorBackendCommands.ts +6 -4
  8. package/front_end/generated/protocol-mapping.d.ts +7 -0
  9. package/front_end/generated/protocol-proxy-api.d.ts +5 -0
  10. package/front_end/generated/protocol.ts +47 -0
  11. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +1 -0
  12. package/front_end/models/emulation/DeviceModeModel.ts +47 -0
  13. package/front_end/models/issues_manager/IssueAggregator.ts +0 -9
  14. package/front_end/models/issues_manager/IssuesManager.ts +0 -5
  15. package/front_end/models/issues_manager/issues_manager.ts +0 -4
  16. package/front_end/models/javascript_metadata/NativeFunctions.js +4 -0
  17. package/front_end/models/lighthouse/lighthouse.ts +9 -0
  18. package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +3 -0
  19. package/front_end/panels/common/AiCodeGenerationTeaser.ts +3 -0
  20. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +1 -6
  21. package/front_end/panels/elements/StylesSidebarPane.ts +79 -0
  22. package/front_end/panels/emulation/DeviceModeToolbar.ts +206 -66
  23. package/front_end/panels/issues/HiddenIssuesRow.ts +1 -1
  24. package/front_end/panels/issues/IssueKindView.ts +2 -1
  25. package/front_end/panels/issues/IssueView.ts +2 -4
  26. package/front_end/panels/issues/IssuesPane.ts +2 -2
  27. package/front_end/panels/lighthouse/LighthouseController.ts +3 -3
  28. package/front_end/panels/lighthouse/LighthousePanel.ts +9 -5
  29. package/front_end/panels/lighthouse/LighthouseProtocolService.ts +5 -5
  30. package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +6 -7
  31. package/front_end/panels/lighthouse/LighthouseReportSelector.ts +4 -3
  32. package/front_end/panels/lighthouse/lighthouse.ts +0 -2
  33. package/front_end/third_party/chromium/README.chromium +1 -1
  34. package/front_end/third_party/source-map-scopes-codec/package/deno.json +1 -1
  35. package/front_end/third_party/source-map-scopes-codec/package/package.json +1 -1
  36. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js +1 -1
  37. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js.map +1 -1
  38. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.ts +1 -1
  39. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js +1 -1
  40. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js.map +1 -1
  41. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.ts +2 -2
  42. package/front_end/ui/legacy/SuggestBox.ts +4 -0
  43. package/front_end/ui/legacy/TextPrompt.ts +1 -1
  44. package/front_end/ui/visual_logging/KnownContextValues.ts +15 -0
  45. package/inspector_overlay/highlight_grid_common.ts +11 -8
  46. package/package.json +1 -1
  47. package/front_end/models/issues_manager/ContrastCheckTrigger.ts +0 -78
  48. package/front_end/models/issues_manager/LowTextContrastIssue.ts +0 -63
  49. package/front_end/panels/issues/AffectedElementsWithLowContrastView.ts +0 -91
  50. /package/front_end/{panels → models}/lighthouse/LighthouseReporterTypes.ts +0 -0
@@ -1221,6 +1221,7 @@ export namespace Audits {
1221
1221
  AutofillAndManualTextPolicyControlledFeaturesInfo = 'AutofillAndManualTextPolicyControlledFeaturesInfo',
1222
1222
  AutofillPolicyControlledFeatureInfo = 'AutofillPolicyControlledFeatureInfo',
1223
1223
  ManualTextPolicyControlledFeatureInfo = 'ManualTextPolicyControlledFeatureInfo',
1224
+ FormModelContextParameterMissingTitleAndDescription = 'FormModelContextParameterMissingTitleAndDescription',
1224
1225
  }
1225
1226
 
1226
1227
  /**
@@ -3033,6 +3034,11 @@ export namespace CSS {
3033
3034
  * The array enumerates @starting-style at-rules starting with the innermost one, going outwards.
3034
3035
  */
3035
3036
  startingStyles?: CSSStartingStyle[];
3037
+ /**
3038
+ * @navigation CSS at-rule array.
3039
+ * The array enumerates @navigation at-rules starting with the innermost one, going outwards.
3040
+ */
3041
+ navigations?: CSSNavigation[];
3036
3042
  }
3037
3043
 
3038
3044
  /**
@@ -3047,6 +3053,7 @@ export namespace CSS {
3047
3053
  ScopeRule = 'ScopeRule',
3048
3054
  StyleRule = 'StyleRule',
3049
3055
  StartingStyleRule = 'StartingStyleRule',
3056
+ NavigationRule = 'NavigationRule',
3050
3057
  }
3051
3058
 
3052
3059
  /**
@@ -3342,6 +3349,29 @@ export namespace CSS {
3342
3349
  styleSheetId?: DOM.StyleSheetId;
3343
3350
  }
3344
3351
 
3352
+ /**
3353
+ * CSS Navigation at-rule descriptor.
3354
+ */
3355
+ export interface CSSNavigation {
3356
+ /**
3357
+ * Navigation rule text.
3358
+ */
3359
+ text: string;
3360
+ /**
3361
+ * Whether the navigation condition is satisfied.
3362
+ */
3363
+ active?: boolean;
3364
+ /**
3365
+ * The associated rule header range in the enclosing stylesheet (if
3366
+ * available).
3367
+ */
3368
+ range?: SourceRange;
3369
+ /**
3370
+ * Identifier of the stylesheet containing this object (if exists).
3371
+ */
3372
+ styleSheetId?: DOM.StyleSheetId;
3373
+ }
3374
+
3345
3375
  /**
3346
3376
  * CSS Scope at-rule descriptor.
3347
3377
  */
@@ -3676,6 +3706,10 @@ export namespace CSS {
3676
3706
  * @supports CSS at-rule condition. Only one type of condition should be set.
3677
3707
  */
3678
3708
  supports?: CSSSupports;
3709
+ /**
3710
+ * @navigation condition. Only one type of condition should be set.
3711
+ */
3712
+ navigation?: CSSNavigation;
3679
3713
  /**
3680
3714
  * Block body.
3681
3715
  */
@@ -4165,6 +4199,19 @@ export namespace CSS {
4165
4199
  supports: CSSSupports;
4166
4200
  }
4167
4201
 
4202
+ export interface SetNavigationTextRequest {
4203
+ styleSheetId: DOM.StyleSheetId;
4204
+ range: SourceRange;
4205
+ text: string;
4206
+ }
4207
+
4208
+ export interface SetNavigationTextResponse extends ProtocolResponseWithError {
4209
+ /**
4210
+ * The resulting CSS Navigation rule after modification.
4211
+ */
4212
+ navigation: CSSNavigation;
4213
+ }
4214
+
4168
4215
  export interface SetScopeTextRequest {
4169
4216
  styleSheetId: DOM.StyleSheetId;
4170
4217
  range: SourceRange;
@@ -363,4 +363,5 @@ export class AiCodeCompletion {
363
363
  export const enum ContextFlavor {
364
364
  CONSOLE = 'console', // generated code can contain console specific APIs like `$0`.
365
365
  SOURCES = 'sources',
366
+ STYLES = 'styles',
366
367
  }
@@ -113,6 +113,7 @@ export class DeviceModeModel extends Common.ObjectWrapper.ObjectWrapper<EventTyp
113
113
  #emulationModel: SDK.EmulationModel.EmulationModel|null;
114
114
  #onModelAvailable: (() => void)|null;
115
115
  #outlineRect?: Rect;
116
+ #screenOrientationLocked: boolean;
116
117
 
117
118
  private constructor() {
118
119
  super();
@@ -172,6 +173,7 @@ export class DeviceModeModel extends Common.ObjectWrapper.ObjectWrapper<EventTyp
172
173
 
173
174
  this.#emulationModel = null;
174
175
  this.#onModelAvailable = null;
176
+ this.#screenOrientationLocked = false;
175
177
  SDK.TargetManager.TargetManager.instance().observeModels(SDK.EmulationModel.EmulationModel, this);
176
178
  }
177
179
 
@@ -442,6 +444,9 @@ export class DeviceModeModel extends Common.ObjectWrapper.ObjectWrapper<EventTyp
442
444
  this.#onModelAvailable = null;
443
445
  callback();
444
446
  }
447
+ emulationModel.addEventListener(
448
+ SDK.EmulationModel.EmulationModelEvents.SCREEN_ORIENTATION_LOCK_CHANGED, this.onScreenOrientationLockChanged,
449
+ this);
445
450
  const resourceTreeModel = emulationModel.target().model(SDK.ResourceTreeModel.ResourceTreeModel);
446
451
  if (resourceTreeModel) {
447
452
  resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.FrameResized, this.onFrameChange, this);
@@ -454,7 +459,12 @@ export class DeviceModeModel extends Common.ObjectWrapper.ObjectWrapper<EventTyp
454
459
 
455
460
  modelRemoved(emulationModel: SDK.EmulationModel.EmulationModel): void {
456
461
  if (this.#emulationModel === emulationModel) {
462
+ emulationModel.removeEventListener(
463
+ SDK.EmulationModel.EmulationModelEvents.SCREEN_ORIENTATION_LOCK_CHANGED, this.onScreenOrientationLockChanged,
464
+ this);
457
465
  this.#emulationModel = null;
466
+ this.#screenOrientationLocked = false;
467
+ this.dispatchEventToListeners(Events.UPDATED);
458
468
  }
459
469
  }
460
470
 
@@ -471,6 +481,43 @@ export class DeviceModeModel extends Common.ObjectWrapper.ObjectWrapper<EventTyp
471
481
  this.showHingeIfApplicable(overlayModel);
472
482
  }
473
483
 
484
+ private onScreenOrientationLockChanged(
485
+ event: Common.EventTarget.EventTargetEvent<SDK.EmulationModel.ScreenOrientationLockChangedEvent>): void {
486
+ this.#screenOrientationLocked = event.data.locked;
487
+ if (event.data.locked && event.data.orientation) {
488
+ this.applyOrientationLock(event.data.orientation);
489
+ }
490
+ this.dispatchEventToListeners(Events.UPDATED);
491
+ }
492
+
493
+ private applyOrientationLock(orientation: Protocol.Emulation.ScreenOrientation): void {
494
+ const wantsLandscape = orientation.type === Protocol.Emulation.ScreenOrientationType.LandscapePrimary ||
495
+ orientation.type === Protocol.Emulation.ScreenOrientationType.LandscapeSecondary;
496
+
497
+ if (this.#type === Type.Device && this.#device && this.#mode) {
498
+ // For device emulation, switch to the matching orientation mode.
499
+ const isCurrentlyLandscape =
500
+ this.#mode.orientation === Horizontal || this.#mode.orientation === HorizontalSpanned;
501
+ if (wantsLandscape !== isCurrentlyLandscape) {
502
+ const rotationPartner = this.#device.getRotationPartner(this.#mode);
503
+ if (rotationPartner) {
504
+ this.emulate(this.#type, this.#device, rotationPartner);
505
+ }
506
+ }
507
+ } else if (this.#type === Type.Responsive) {
508
+ // For responsive mode, swap width/height if orientation doesn't match.
509
+ const appliedSize = this.appliedDeviceSize();
510
+ const isCurrentlyLandscape = appliedSize.width > appliedSize.height;
511
+ if (wantsLandscape !== isCurrentlyLandscape) {
512
+ this.setSizeAndScaleToFit(appliedSize.height, appliedSize.width);
513
+ }
514
+ }
515
+ }
516
+
517
+ isScreenOrientationLocked(): boolean {
518
+ return this.#screenOrientationLocked;
519
+ }
520
+
474
521
  private scaleSettingChanged(): void {
475
522
  this.calculateAndEmulate(false);
476
523
  }
@@ -17,7 +17,6 @@ import {HeavyAdIssue} from './HeavyAdIssue.js';
17
17
  import {Issue, IssueCategory, IssueKind, unionIssueKind} from './Issue.js';
18
18
  import type {EventTypes as IssuesManagerEventsTypes, IssueAddedEvent} from './IssuesManager.js';
19
19
  import {Events as IssuesManagerEvents} from './IssuesManagerEvents.js';
20
- import {LowTextContrastIssue} from './LowTextContrastIssue.js';
21
20
  import type {MarkdownIssueDescription} from './MarkdownIssueDescription.js';
22
21
  import {MixedContentIssue} from './MixedContentIssue.js';
23
22
  import {PartitioningBlobURLIssue} from './PartitioningBlobURLIssue.js';
@@ -64,7 +63,6 @@ export class AggregatedIssue extends Issue {
64
63
  #cspIssues = new Set<ContentSecurityPolicyIssue>();
65
64
  #deprecationIssues = new Set<DeprecationIssue>();
66
65
  #issueKind = IssueKind.IMPROVEMENT;
67
- #lowContrastIssues = new Set<LowTextContrastIssue>();
68
66
  #cookieDeprecationMetadataIssues = new Set<CookieDeprecationMetadataIssue>();
69
67
  #mixedContentIssues = new Set<MixedContentIssue>();
70
68
  #partitioningBlobURLIssues = new Set<PartitioningBlobURLIssue>();
@@ -142,10 +140,6 @@ export class AggregatedIssue extends Issue {
142
140
  return this.#deprecationIssues;
143
141
  }
144
142
 
145
- getLowContrastIssues(): Iterable<LowTextContrastIssue> {
146
- return this.#lowContrastIssues;
147
- }
148
-
149
143
  override requests(): Iterable<Protocol.Audits.AffectedRequest> {
150
144
  return this.#affectedRequests.values();
151
145
  }
@@ -267,9 +261,6 @@ export class AggregatedIssue extends Issue {
267
261
  if (issue instanceof SharedArrayBufferIssue) {
268
262
  this.#sharedArrayBufferIssues.add(issue);
269
263
  }
270
- if (issue instanceof LowTextContrastIssue) {
271
- this.#lowContrastIssues.add(issue);
272
- }
273
264
  if (issue instanceof CorsIssue) {
274
265
  this.#corsIssues.add(issue);
275
266
  }
@@ -22,7 +22,6 @@ import {GenericIssue} from './GenericIssue.js';
22
22
  import {HeavyAdIssue} from './HeavyAdIssue.js';
23
23
  import type {Issue, IssueKind} from './Issue.js';
24
24
  import {Events} from './IssuesManagerEvents.js';
25
- import {LowTextContrastIssue} from './LowTextContrastIssue.js';
26
25
  import {MixedContentIssue} from './MixedContentIssue.js';
27
26
  import {PartitioningBlobURLIssue} from './PartitioningBlobURLIssue.js';
28
27
  import {PermissionElementIssue} from './PermissionElementIssue.js';
@@ -81,10 +80,6 @@ const issueCodeHandlers = new Map<
81
80
  Protocol.Audits.InspectorIssueCode.SharedDictionaryIssue,
82
81
  SharedDictionaryIssue.fromInspectorIssue,
83
82
  ],
84
- [
85
- Protocol.Audits.InspectorIssueCode.LowTextContrastIssue,
86
- LowTextContrastIssue.fromInspectorIssue,
87
- ],
88
83
  [
89
84
  Protocol.Audits.InspectorIssueCode.CorsIssue,
90
85
  CorsIssue.fromInspectorIssue,
@@ -7,7 +7,6 @@ import * as CheckFormsIssuesTrigger from './CheckFormsIssuesTrigger.js';
7
7
  import * as ClientHintIssue from './ClientHintIssue.js';
8
8
  import * as ConnectionAllowlistIssue from './ConnectionAllowlistIssue.js';
9
9
  import * as ContentSecurityPolicyIssue from './ContentSecurityPolicyIssue.js';
10
- import * as ContrastCheckTrigger from './ContrastCheckTrigger.js';
11
10
  import * as CookieDeprecationMetadataIssue from './CookieDeprecationMetadataIssue.js';
12
11
  import * as CookieIssue from './CookieIssue.js';
13
12
  import * as CorsIssue from './CorsIssue.js';
@@ -21,7 +20,6 @@ import * as Issue from './Issue.js';
21
20
  import * as IssueAggregator from './IssueAggregator.js';
22
21
  import * as IssueResolver from './IssueResolver.js';
23
22
  import * as IssuesManager from './IssuesManager.js';
24
- import * as LowTextContrastIssue from './LowTextContrastIssue.js';
25
23
  import * as MarkdownIssueDescription from './MarkdownIssueDescription.js';
26
24
  import * as MixedContentIssue from './MixedContentIssue.js';
27
25
  import * as PartitioningBlobURLIssue from './PartitioningBlobURLIssue.js';
@@ -42,7 +40,6 @@ export {
42
40
  ClientHintIssue,
43
41
  ConnectionAllowlistIssue,
44
42
  ContentSecurityPolicyIssue,
45
- ContrastCheckTrigger,
46
43
  CookieDeprecationMetadataIssue,
47
44
  CookieIssue,
48
45
  CorsIssue,
@@ -56,7 +53,6 @@ export {
56
53
  IssueAggregator,
57
54
  IssueResolver,
58
55
  IssuesManager,
59
- LowTextContrastIssue,
60
56
  MarkdownIssueDescription,
61
57
  MixedContentIssue,
62
58
  PartitioningBlobURLIssue,
@@ -7221,6 +7221,10 @@ export const NativeFunctions = [
7221
7221
  name: "OffscreenCanvas",
7222
7222
  signatures: [["width","height"]]
7223
7223
  },
7224
+ {
7225
+ name: "OverscrollEvent",
7226
+ signatures: [["type","?eventInitDict"]]
7227
+ },
7224
7228
  {
7225
7229
  name: "setValueAndClosePopup",
7226
7230
  signatures: [["numberValue","stringValue","is_keyboard_event"]]
@@ -0,0 +1,9 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as ReporterTypes from './LighthouseReporterTypes.js';
6
+
7
+ export {
8
+ ReporterTypes,
9
+ };
@@ -70,6 +70,9 @@ function getTooltipDisclaimerText(noLogging: boolean, panel: AiCodeCompletion.Ai
70
70
  return noLogging ?
71
71
  lockedString(UIStringsNotTranslate.tooltipDisclaimerTextForAiCodeCompletionNoLoggingInSources) :
72
72
  lockedString(UIStringsNotTranslate.tooltipDisclaimerTextForAiCodeCompletionInSources);
73
+ case AiCodeCompletion.AiCodeCompletion.ContextFlavor.STYLES:
74
+ // TODO(476101019): update with string for styles pane
75
+ return '';
73
76
  }
74
77
  }
75
78
 
@@ -147,6 +147,9 @@ function getTooltipDisclaimerText(noLogging: boolean, panel: AiCodeCompletion.Ai
147
147
  return noLogging ?
148
148
  lockedString(UIStringsNotTranslate.tooltipDisclaimerTextForAiCodeGenerationNoLoggingInSources) :
149
149
  lockedString(UIStringsNotTranslate.tooltipDisclaimerTextForAiCodeGenerationInSources);
150
+ case AiCodeCompletion.AiCodeCompletion.ContextFlavor.STYLES:
151
+ // TODO(476101019): update with string for styles pane
152
+ return '';
150
153
  }
151
154
  }
152
155
 
@@ -18,11 +18,6 @@ export class StylesAiCodeCompletionProvider {
18
18
 
19
19
  #boundOnUpdateAiCodeCompletionState = this.#updateAiCodeCompletionState.bind(this);
20
20
 
21
- #debouncedRequestAidaSuggestion =
22
- Common.Debouncer.debounce((prefix: string, suffix: string, cursorPositionAtRequest: number) => {
23
- void this.#requestAidaSuggestion(prefix, suffix, cursorPositionAtRequest);
24
- }, TextEditor.AiCodeCompletionProvider.AIDA_REQUEST_DEBOUNCE_TIMEOUT_MS);
25
-
26
21
  private constructor(aiCodeCompletionConfig: TextEditor.AiCodeCompletionProvider.AiCodeCompletionConfig) {
27
22
  const devtoolsLocale = i18n.DevToolsLocale.DevToolsLocale.instance();
28
23
  if (!AiCodeCompletion.AiCodeCompletion.AiCodeCompletion.isAiCodeCompletionStylesEnabled(devtoolsLocale.locale)) {
@@ -113,7 +108,7 @@ export class StylesAiCodeCompletionProvider {
113
108
  prefix = prefix + text;
114
109
  const suffix = content.substring(propertyEndOffset);
115
110
  // TODO(b/476098133): Consider adjusting cursor position
116
- this.#debouncedRequestAidaSuggestion(prefix, suffix, cursorPosition);
111
+ await this.#requestAidaSuggestion(prefix, suffix, cursorPosition);
117
112
  }
118
113
 
119
114
  async #requestAidaSuggestion(prefix: string, suffix: string, cursorPositionAtRequest: number): Promise<void> {
@@ -42,9 +42,11 @@ import {assertNotNullOrUndefined} from '../../core/platform/platform.js';
42
42
  import * as Root from '../../core/root/root.js';
43
43
  import * as SDK from '../../core/sdk/sdk.js';
44
44
  import * as Protocol from '../../generated/protocol.js';
45
+ import * as AiCodeCompletion from '../../models/ai_code_completion/ai_code_completion.js';
45
46
  import * as Bindings from '../../models/bindings/bindings.js';
46
47
  import type * as ComputedStyle from '../../models/computed_style/computed_style.js';
47
48
  import * as TextUtils from '../../models/text_utils/text_utils.js';
49
+ import * as TextEditor from '../../ui/components/text_editor/text_editor.js';
48
50
  import {createIcon, Icon} from '../../ui/kit/kit.js';
49
51
  import * as InlineEditor from '../../ui/legacy/components/inline_editor/inline_editor.js';
50
52
  import * as Components from '../../ui/legacy/components/utils/utils.js';
@@ -70,6 +72,7 @@ import {
70
72
  } from './StylePropertiesSection.js';
71
73
  import {StylePropertyHighlighter} from './StylePropertyHighlighter.js';
72
74
  import type {StylePropertyTreeElement} from './StylePropertyTreeElement.js';
75
+ import * as StylesAiCodeCompletionProvider from './StylesAiCodeCompletionProvider.js';
73
76
  import type {StylesContainer} from './StylesContainer.js';
74
77
  import stylesSidebarPaneStyles from './stylesSidebarPane.css.js';
75
78
  import {WebCustomData} from './WebCustomData.js';
@@ -1736,6 +1739,12 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
1736
1739
  private treeElement: StylePropertyTreeElement;
1737
1740
  private isEditingName: boolean;
1738
1741
  private readonly cssVariables: string[];
1742
+ aiCodeCompletionConfig?: TextEditor.AiCodeCompletionProvider.AiCodeCompletionConfig;
1743
+ aiCodeCompletionProvider?: StylesAiCodeCompletionProvider.StylesAiCodeCompletionProvider;
1744
+
1745
+ #debouncedTriggerAiCodeCompletion = Common.Debouncer.debounce(() => {
1746
+ void this.triggerAiCodeCompletion();
1747
+ }, TextEditor.AiCodeCompletionProvider.AIDA_REQUEST_DEBOUNCE_TIMEOUT_MS);
1739
1748
 
1740
1749
  constructor(treeElement: StylePropertyTreeElement, isEditingName: boolean, completions: string[] = []) {
1741
1750
  // Use the same callback both for applyItemCallback and acceptItemCallback.
@@ -1794,6 +1803,27 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
1794
1803
  }
1795
1804
  }
1796
1805
  }
1806
+
1807
+ const devtoolsLocale = i18n.DevToolsLocale.DevToolsLocale.instance();
1808
+ if (AiCodeCompletion.AiCodeCompletion.AiCodeCompletion.isAiCodeCompletionStylesEnabled(devtoolsLocale.locale)) {
1809
+ this.aiCodeCompletionConfig = {
1810
+ completionContext: {},
1811
+ generationContext: {},
1812
+ onFeatureEnabled: () => {},
1813
+ onFeatureDisabled: () => {},
1814
+ onSuggestionAccepted: () => {},
1815
+ onRequestTriggered: () => {},
1816
+ onResponseReceived: () => {},
1817
+ panel: AiCodeCompletion.AiCodeCompletion.ContextFlavor.STYLES,
1818
+ getCompletionHint: this.getCompletionHint.bind(this),
1819
+ getCurrentText: () => {
1820
+ return this.text();
1821
+ },
1822
+ setAiAutoCompletion: () => {},
1823
+ };
1824
+ this.aiCodeCompletionProvider =
1825
+ StylesAiCodeCompletionProvider.StylesAiCodeCompletionProvider.createInstance(this.aiCodeCompletionConfig);
1826
+ }
1797
1827
  }
1798
1828
 
1799
1829
  override onKeyDown(event: Event): void {
@@ -1844,6 +1874,13 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
1844
1874
  return false;
1845
1875
  }
1846
1876
 
1877
+ override onInput(event: Event): void {
1878
+ super.onInput(event);
1879
+ if (this.aiCodeCompletionProvider) {
1880
+ this.#debouncedTriggerAiCodeCompletion();
1881
+ }
1882
+ }
1883
+
1847
1884
  private handleNameOrValueUpDown(event: Event): boolean {
1848
1885
  function finishHandler(this: CSSPropertyPrompt, _originalValue: string, _replacementString: string): void {
1849
1886
  // Synthesize property text disregarding any comments, custom whitespace etc.
@@ -2054,6 +2091,48 @@ export class CSSPropertyPrompt extends UI.TextPrompt.TextPrompt {
2054
2091
  return subtitleElement;
2055
2092
  }
2056
2093
  }
2094
+
2095
+ private async triggerAiCodeCompletion(): Promise<void> {
2096
+ const selection = this.element().getComponentSelection();
2097
+ if (!this.aiCodeCompletionProvider || !selection || selection.rangeCount === 0) {
2098
+ return;
2099
+ }
2100
+ const range = selection.getRangeAt(0);
2101
+ const userInput = this.text();
2102
+ // Only trigger if caret is at end of text content
2103
+ if (range.endOffset < userInput.length) {
2104
+ return;
2105
+ }
2106
+ const cssModel = this.treeElement.stylesContainer().cssModel();
2107
+ if (!cssModel) {
2108
+ return;
2109
+ }
2110
+ await this.aiCodeCompletionProvider.triggerAiCodeCompletion(
2111
+ userInput, range.endOffset, this.isEditingName, this.treeElement.property, cssModel);
2112
+ }
2113
+
2114
+ /**
2115
+ * Extracts the remaining portion of the suggestion text that follows the
2116
+ * user's current input.
2117
+ */
2118
+ private getCompletionHint(): string|null {
2119
+ const topSuggestion = this.isSuggestBoxVisible() ? this.suggestBox?.completion() : null;
2120
+ const suggestionText = topSuggestion?.text;
2121
+ if (!suggestionText) {
2122
+ return null;
2123
+ }
2124
+ const userInput = this.text();
2125
+ let completionHint = suggestionText;
2126
+ // Iterate from the longest possible overlap down to the shortest
2127
+ for (let i = Math.min(userInput.length, suggestionText.length); i > 0; i--) {
2128
+ const overlapCandidate = suggestionText.substring(0, i);
2129
+ if (userInput.endsWith(overlapCandidate)) {
2130
+ completionHint = suggestionText.slice(i);
2131
+ break;
2132
+ }
2133
+ }
2134
+ return completionHint;
2135
+ }
2057
2136
  }
2058
2137
 
2059
2138
  export function unescapeCssString(input: string): string {