chrome-devtools-frontend 1.0.1581449 → 1.0.1582745

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 (95) hide show
  1. package/agents/prompts/merging-devtools-module.md +144 -0
  2. package/agents/prompts/ui-widgets.md +351 -0
  3. package/agents/prompts/verification.md +2 -1
  4. package/docs/contributing/README.md +5 -6
  5. package/docs/contributing/changes.md +1 -2
  6. package/docs/styleguide/ux/README.md +1 -1
  7. package/front_end/core/sdk/OverlayModel.ts +4 -2
  8. package/front_end/core/sdk/RemoteObject.ts +7 -1
  9. package/front_end/core/sdk/StorageKeyManager.ts +6 -1
  10. package/front_end/core/sdk/Target.ts +4 -2
  11. package/front_end/entrypoint_template.html +5 -1
  12. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +31 -40
  13. package/front_end/entrypoints/greendev_floaty/floaty.css +41 -1
  14. package/front_end/entrypoints/greendev_floaty/floaty.html +8 -1
  15. package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +5 -5
  16. package/front_end/entrypoints/node_app/app/NodeMain.ts +19 -1
  17. package/front_end/entrypoints/node_app/node_app.ts +34 -0
  18. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  19. package/front_end/generated/SupportedCSSProperties.js +2 -0
  20. package/front_end/generated/protocol.ts +0 -6
  21. package/front_end/models/ai_assistance/AiConversation.ts +10 -0
  22. package/front_end/models/ai_assistance/agents/AiAgent.ts +2 -0
  23. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +26 -4
  24. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +85 -7
  25. package/front_end/models/ai_assistance/agents/NetworkAgent.ts +2 -6
  26. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +66 -2
  27. package/front_end/models/computed_style/ComputedStyleModel.ts +26 -0
  28. package/front_end/models/greendev/Prototypes.ts +1 -10
  29. package/front_end/models/issues_manager/ConnectionAllowlistIssue.ts +75 -0
  30. package/front_end/models/issues_manager/CookieIssue.ts +0 -28
  31. package/front_end/models/issues_manager/FederatedAuthRequestIssue.ts +0 -30
  32. package/front_end/models/issues_manager/IssuesManager.ts +5 -0
  33. package/front_end/models/issues_manager/descriptions/connectionAllowlistInvalidAllowlistItemType.md +12 -0
  34. package/front_end/models/issues_manager/descriptions/connectionAllowlistInvalidHeader.md +12 -0
  35. package/front_end/models/issues_manager/descriptions/connectionAllowlistInvalidUrlPattern.md +8 -0
  36. package/front_end/models/issues_manager/descriptions/connectionAllowlistItemNotInnerList.md +12 -0
  37. package/front_end/models/issues_manager/descriptions/connectionAllowlistMoreThanOneList.md +7 -0
  38. package/front_end/models/issues_manager/descriptions/connectionAllowlistReportingEndpointNotToken.md +10 -0
  39. package/front_end/models/issues_manager/issues_manager.ts +2 -0
  40. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +93 -6
  41. package/front_end/panels/ai_assistance/components/ChatInput.ts +8 -4
  42. package/front_end/panels/application/ApplicationPanelSidebar.ts +13 -11
  43. package/front_end/panels/application/DOMStorageModel.ts +1 -1
  44. package/front_end/panels/application/ResourcesPanel.ts +10 -5
  45. package/front_end/panels/application/preloading/PreloadingView.ts +8 -1
  46. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +4 -1
  47. package/front_end/panels/application/preloading/components/PreloadingGrid.ts +2 -1
  48. package/front_end/panels/application/preloading/components/PreloadingString.ts +12 -3
  49. package/front_end/panels/application/preloading/helper/PreloadingForward.ts +14 -0
  50. package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +37 -3
  51. package/front_end/panels/changes/ChangesSidebar.ts +2 -6
  52. package/front_end/panels/common/AiCodeCompletionTeaser.ts +13 -3
  53. package/front_end/panels/common/aiCodeCompletionTeaser.css +6 -0
  54. package/front_end/panels/console/ConsoleSidebar.ts +3 -11
  55. package/front_end/panels/console_counters/WarningErrorCounter.ts +16 -10
  56. package/front_end/panels/elements/ComputedStyleWidget.ts +55 -37
  57. package/front_end/panels/elements/PlatformFontsWidget.ts +23 -10
  58. package/front_end/panels/greendev/GreenDevPanel.css +42 -1
  59. package/front_end/panels/greendev/GreenDevPanel.ts +30 -1
  60. package/front_end/panels/lighthouse/LighthouseStartView.ts +3 -5
  61. package/front_end/panels/lighthouse/lighthouseStartView.css +6 -0
  62. package/front_end/panels/network/NetworkLogView.ts +6 -6
  63. package/front_end/panels/network/RequestInitiatorView.ts +27 -19
  64. package/front_end/panels/network/RequestTimingView.ts +1 -1
  65. package/front_end/panels/settings/AISettingsTab.ts +1 -5
  66. package/front_end/panels/settings/KeybindsSettingsTab.ts +4 -3
  67. package/front_end/panels/settings/SettingsScreen.ts +0 -51
  68. package/front_end/panels/sources/OutlineQuickOpen.ts +19 -0
  69. package/front_end/panels/timeline/AnimationsTrackAppender.ts +4 -1
  70. package/front_end/panels/timeline/InteractionsTrackAppender.ts +1 -1
  71. package/front_end/panels/timeline/TimelinePanel.ts +25 -0
  72. package/front_end/panels/timeline/TimelineUIUtils.ts +13 -16
  73. package/front_end/third_party/chromium/README.chromium +1 -1
  74. package/front_end/third_party/lighthouse/README.chromium +2 -2
  75. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +145 -144
  76. package/front_end/third_party/lighthouse/report/bundle.js +12 -5
  77. package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +2 -2
  78. package/front_end/ui/components/markdown_view/MarkdownLinksMap.ts +5 -1
  79. package/front_end/ui/legacy/ListControl.ts +28 -1
  80. package/front_end/ui/legacy/Toolbar.ts +4 -4
  81. package/front_end/ui/legacy/Treeoutline.ts +5 -5
  82. package/front_end/ui/legacy/UIUtils.ts +26 -10
  83. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +10 -13
  84. package/front_end/ui/legacy/components/utils/Linkifier.ts +4 -7
  85. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
  86. package/inspector_overlay/main.ts +18 -3
  87. package/inspector_overlay/tool_green_dev_anchors.css +54 -0
  88. package/inspector_overlay/tool_green_dev_anchors.ts +164 -0
  89. package/inspector_overlay/tool_persistent.ts +14 -0
  90. package/package.json +1 -1
  91. package/docs/contributing/design.md +0 -166
  92. package/docs/design_guidelines.md +0 -1
  93. package/front_end/models/issues_manager/descriptions/federatedAuthRequestClientMetadataHttpNotFound.md +0 -1
  94. package/front_end/models/issues_manager/descriptions/federatedAuthRequestClientMetadataInvalidResponse.md +0 -1
  95. package/front_end/models/issues_manager/descriptions/federatedAuthRequestClientMetadataNoResponse.md +0 -1
@@ -58,9 +58,12 @@ export const markdownLinks = new Map<string, string>([
58
58
  ['signatureHeader', 'https://www.rfc-editor.org/rfc/rfc9421.html#name-the-signature-http-field'],
59
59
  ['signatureInputHeader', 'https://www.rfc-editor.org/rfc/rfc9421.html#name-the-signature-input-http-fi'],
60
60
  ['signatureParameters', 'https://www.rfc-editor.org/rfc/rfc9421.html#name-signature-parameters'],
61
- ['sfDictionary', 'https://www.rfc-editor.org/rfc/rfc8941.html#name-dictionaries'],
62
61
  ['sfByteSequence', 'https://www.rfc-editor.org/rfc/rfc8941.html#name-byte-sequences'],
62
+ ['sfDictionary', 'https://www.rfc-editor.org/rfc/rfc8941.html#name-dictionaries'],
63
63
  ['sfInnerList', 'https://www.rfc-editor.org/rfc/rfc8941.html#name-inner-lists'],
64
+ ['sfList', 'https://www.rfc-editor.org/rfc/rfc8941.html#name-lists'],
65
+ ['sfString', 'https://www.rfc-editor.org/rfc/rfc8941.html#name-strings'],
66
+ ['sfToken', 'https://www.rfc-editor.org/rfc/rfc8941.html#name-tokens'],
64
67
  ['componentParameterSf', 'https://www.rfc-editor.org/rfc/rfc9421.html#name-strict-serialization-of-htt'],
65
68
  ['componentParameterReq', 'https://www.rfc-editor.org/rfc/rfc9421.html#content-request-response'],
66
69
  [
@@ -74,6 +77,7 @@ export const markdownLinks = new Map<string, string>([
74
77
  'https://privacysandbox.com/news/update-on-plans-for-privacy-sandbox-technologies/',
75
78
  'https://privacysandbox.com/news/update-on-plans-for-privacy-sandbox-technologies/'
76
79
  ],
80
+ ['urlPatternSpec', 'https://urlpattern.spec.whatwg.org/'],
77
81
  ]);
78
82
 
79
83
  export const getMarkdownLink = (key: string): string => {
@@ -282,6 +282,26 @@ export class ListControl<T> {
282
282
  return false;
283
283
  }
284
284
 
285
+ selectFirstItem(center?: boolean): boolean {
286
+ const index = this.findFirstSelectable(0, +1, false);
287
+ if (index !== -1) {
288
+ this.scrollIntoView(index, center);
289
+ this.select(index);
290
+ return true;
291
+ }
292
+ return false;
293
+ }
294
+
295
+ selectLastItem(center?: boolean): boolean {
296
+ const index = this.findFirstSelectable(this.model.length - 1, -1, false);
297
+ if (index !== -1) {
298
+ this.scrollIntoView(index, center);
299
+ this.select(index);
300
+ return true;
301
+ }
302
+ return false;
303
+ }
304
+
285
305
  private scrollIntoView(index: number, center?: boolean): void {
286
306
  if (this.mode === ListMode.NonViewport) {
287
307
  this.elementAtIndex(index).scrollIntoViewIfNeeded(Boolean(center));
@@ -328,6 +348,12 @@ export class ListControl<T> {
328
348
  case 'PageDown':
329
349
  selected = this.selectItemNextPage(false);
330
350
  break;
351
+ case 'Home':
352
+ selected = this.selectFirstItem();
353
+ break;
354
+ case 'End':
355
+ selected = this.selectLastItem();
356
+ break;
331
357
  }
332
358
  if (selected) {
333
359
  event.consume(true);
@@ -364,7 +390,8 @@ export class ListControl<T> {
364
390
  element = this.delegate.createElementForItem(item);
365
391
  if (!element.hasAttribute('jslog')) {
366
392
  element.setAttribute(
367
- 'jslog', `${VisualLogging.item().track({click: true, keydown: 'ArrowUp|ArrowDown|PageUp|PageDown'})}`);
393
+ 'jslog',
394
+ `${VisualLogging.item().track({click: true, keydown: 'ArrowUp|ArrowDown|PageUp|PageDown|Home|End'})}`);
368
395
  }
369
396
  this.itemToElement.set(item, element);
370
397
  this.updateElementARIA(element, index);
@@ -1321,11 +1321,11 @@ export class ToolbarCheckbox extends ToolbarItem<void> {
1321
1321
  constructor(
1322
1322
  text: Common.UIString.LocalizedString, tooltip?: Common.UIString.LocalizedString,
1323
1323
  listener?: ((arg0: MouseEvent) => void), jslogContext?: string) {
1324
- const checkboxLabel = CheckboxLabel.create(text, undefined, undefined, jslogContext);
1324
+ // Pass tooltip to CheckboxLabel.create so it's set on the inner input/text elements,
1325
+ // rather than installing it on the wrapper element which causes screen readers to
1326
+ // incorrectly announce it as a group name.
1327
+ const checkboxLabel = CheckboxLabel.create(text, undefined, undefined, jslogContext, undefined, tooltip);
1325
1328
  super(checkboxLabel);
1326
- if (tooltip) {
1327
- Tooltip.install(this.element, tooltip);
1328
- }
1329
1329
  if (listener) {
1330
1330
  this.element.addEventListener('click', listener, false);
1331
1331
  }
@@ -1566,7 +1566,7 @@ class TreeViewTreeElement extends TreeElement {
1566
1566
  this.listItemElement.classList.add(className);
1567
1567
  this.#clonedClasses.add(className);
1568
1568
  }
1569
- InterceptBindingDirective.attachEventListeners(this.configElement, this.listItemElement);
1569
+ InterceptBindingDirective.setEventListeners(this.configElement, this.listItemElement);
1570
1570
 
1571
1571
  for (const child of this.configElement.childNodes) {
1572
1572
  if (child instanceof HTMLUListElement && child.role === 'group') {
@@ -1704,7 +1704,7 @@ export class TreeViewElement extends HTMLElementWithLightDOMTemplate {
1704
1704
  super();
1705
1705
  this.#treeOutline.addEventListener(Events.ElementSelected, event => {
1706
1706
  if (event.data instanceof TreeViewTreeElement) {
1707
- this.dispatchEvent(new TreeViewElement.SelectEvent(event.data.configElement));
1707
+ event.data.listItemElement.dispatchEvent(new TreeViewElement.SelectEvent());
1708
1708
  }
1709
1709
  });
1710
1710
  this.#treeOutline.addEventListener(Events.ElementExpanded, event => {
@@ -1859,9 +1859,9 @@ export class TreeViewElement extends HTMLElementWithLightDOMTemplate {
1859
1859
  }
1860
1860
 
1861
1861
  export namespace TreeViewElement {
1862
- export class SelectEvent extends CustomEvent<HTMLLIElement> {
1863
- constructor(detail: HTMLLIElement) {
1864
- super('select', {detail});
1862
+ export class SelectEvent extends CustomEvent<void> {
1863
+ constructor() {
1864
+ super('select');
1865
1865
  }
1866
1866
  }
1867
1867
 
@@ -1173,7 +1173,7 @@ export class CheckboxLabel extends HTMLElement {
1173
1173
 
1174
1174
  static create(
1175
1175
  title?: Platform.UIString.LocalizedString, checked?: boolean, subtitle?: Platform.UIString.LocalizedString,
1176
- jslogContext?: string, small?: boolean): CheckboxLabel {
1176
+ jslogContext?: string, small?: boolean, tooltip?: Platform.UIString.LocalizedString): CheckboxLabel {
1177
1177
  const element = document.createElement('devtools-checkbox');
1178
1178
  element.#checkboxElement.checked = Boolean(checked);
1179
1179
  if (jslogContext) {
@@ -1182,11 +1182,17 @@ export class CheckboxLabel extends HTMLElement {
1182
1182
  }
1183
1183
  if (title !== undefined) {
1184
1184
  element.#textElement.textContent = title;
1185
- element.#checkboxElement.title = title;
1186
1185
  if (subtitle !== undefined) {
1187
1186
  element.#textElement.createChild('div', 'devtools-checkbox-subtitle').textContent = subtitle;
1188
1187
  }
1189
1188
  }
1189
+ // checkboxElement tooltip: tooltip first, then title (custom tooltip takes precedence for the input)
1190
+ const inputTooltip = tooltip ?? title;
1191
+ if (inputTooltip) {
1192
+ element.#checkboxElement.title = inputTooltip;
1193
+ // Set aria-description for screen reader announcement
1194
+ element.#checkboxElement.setAttribute('aria-description', inputTooltip);
1195
+ }
1190
1196
  element.#checkboxElement.classList.toggle('small', small);
1191
1197
  return element;
1192
1198
  }
@@ -1970,6 +1976,7 @@ export function bindToAction(actionName: string): ReturnType<typeof Directives.r
1970
1976
  type BindingEventListener = (arg: any) => any;
1971
1977
  export class InterceptBindingDirective extends Lit.Directive.Directive {
1972
1978
  static readonly #interceptedBindings = new WeakMap<Element, Map<string, BindingEventListener>>();
1979
+ static readonly #attachedBindings = new WeakMap<Element, Map<string, BindingEventListener>>();
1973
1980
 
1974
1981
  override update(part: Lit.Directive.Part, [listener]: [BindingEventListener]): unknown {
1975
1982
  if (part.type !== Lit.Directive.PartType.EVENT) {
@@ -1990,13 +1997,22 @@ export class InterceptBindingDirective extends Lit.Directive.Directive {
1990
1997
  return undefined;
1991
1998
  }
1992
1999
 
1993
- static attachEventListeners(templateElement: Element, renderedElement: Element): void {
1994
- const eventListeners = InterceptBindingDirective.#interceptedBindings.get(templateElement);
1995
- if (!eventListeners) {
1996
- return;
2000
+ static setEventListeners(templateElement: Element, renderedElement: Element): void {
2001
+ const attachedListeners = InterceptBindingDirective.#attachedBindings.get(renderedElement);
2002
+ if (attachedListeners) {
2003
+ for (const [name, listener] of attachedListeners) {
2004
+ renderedElement.removeEventListener(name, listener);
2005
+ }
1997
2006
  }
1998
- for (const [name, listener] of eventListeners) {
1999
- renderedElement.addEventListener(name, listener);
2007
+
2008
+ const newListeners = InterceptBindingDirective.#interceptedBindings.get(templateElement);
2009
+ if (newListeners?.size) {
2010
+ for (const [name, listener] of newListeners) {
2011
+ renderedElement.addEventListener(name, listener);
2012
+ }
2013
+ InterceptBindingDirective.#attachedBindings.set(renderedElement, new Map(newListeners));
2014
+ } else {
2015
+ InterceptBindingDirective.#attachedBindings.delete(renderedElement);
2000
2016
  }
2001
2017
  }
2002
2018
  }
@@ -2029,7 +2045,7 @@ export class HTMLElementWithLightDOMTemplate extends HTMLElement {
2029
2045
  clone.appendChild(HTMLElementWithLightDOMTemplate.cloneNode(child));
2030
2046
  }
2031
2047
  if (node instanceof Element && clone instanceof Element) {
2032
- InterceptBindingDirective.attachEventListeners(node, clone);
2048
+ InterceptBindingDirective.setEventListeners(node, clone);
2033
2049
  }
2034
2050
  return clone;
2035
2051
  }
@@ -2059,7 +2075,7 @@ export class HTMLElementWithLightDOMTemplate extends HTMLElement {
2059
2075
  HTMLElementWithLightDOMTemplate.patchLitTemplate(value);
2060
2076
  return value;
2061
2077
  }
2062
- if (Array.isArray(value)) {
2078
+ if (Array.isArray(value) || value instanceof Iterator) {
2063
2079
  return value.map(patchValue);
2064
2080
  }
2065
2081
 
@@ -96,7 +96,7 @@ function populateContextMenu(link: Element, event: Event): void {
96
96
  }
97
97
 
98
98
  // TODO(crbug.com/456517732): remove when all usages of runtimeStackTrace are migrated.
99
- export function buildStackTraceRowsForLegacyRuntimeStackTrace(
99
+ function buildStackTraceRowsForLegacyRuntimeStackTrace(
100
100
  stackTrace: Protocol.Runtime.StackTrace,
101
101
  target: SDK.Target.Target|null,
102
102
  linkifier: Linkifier,
@@ -159,7 +159,7 @@ export function buildStackTraceRowsForLegacyRuntimeStackTrace(
159
159
  return stackTraceRows;
160
160
  }
161
161
 
162
- export function buildStackTraceRows(
162
+ function buildStackTraceRows(
163
163
  stackTrace: StackTrace.StackTrace.StackTrace,
164
164
  target: SDK.Target.Target|null,
165
165
  linkifier: Linkifier,
@@ -189,14 +189,9 @@ export function buildStackTraceRows(
189
189
  inlineFrameIndex: 0,
190
190
  revealBreakpoint: previousStackFrameWasBreakpointCondition,
191
191
  });
192
- if (link) {
193
- link.setAttribute('jslog', `${VisualLogging.link('stack-trace').track({click: true})}`);
194
- link.addEventListener('contextmenu', populateContextMenu.bind(null, link));
192
+ link.setAttribute('jslog', `${VisualLogging.link('stack-trace').track({click: true})}`);
193
+ link.addEventListener('contextmenu', populateContextMenu.bind(null, link));
195
194
 
196
- if (!link.textContent) {
197
- link.textContent = i18nString(UIStrings.unknownSource);
198
- }
199
- }
200
195
  stackTraceRows.push({functionName, link});
201
196
  previousStackFrameWasBreakpointCondition = [
202
197
  SDK.DebuggerModel.COND_BREAKPOINT_SOURCE_URL,
@@ -306,12 +301,12 @@ export interface Options {
306
301
  expandable?: boolean;
307
302
  }
308
303
 
309
- export interface StackTraceRegularRow {
304
+ interface StackTraceRegularRow {
310
305
  functionName: string;
311
306
  link: HTMLElement|null;
312
307
  }
313
308
 
314
- export interface StackTraceAsyncRow {
309
+ interface StackTraceAsyncRow {
315
310
  asyncDescription: string;
316
311
  }
317
312
 
@@ -353,8 +348,6 @@ export class StackTracePreviewContent extends UI.Widget.Widget {
353
348
  this.#table = this.contentElement.createChild('table', 'stack-preview-container');
354
349
  this.#table.classList.toggle('width-constrained', this.#options.widthConstrained ?? false);
355
350
 
356
- this.#stackTrace?.addEventListener(StackTrace.StackTrace.Events.UPDATED, this.performUpdate.bind(this));
357
-
358
351
  this.performUpdate();
359
352
  }
360
353
 
@@ -407,7 +400,11 @@ export class StackTracePreviewContent extends UI.Widget.Widget {
407
400
  }
408
401
 
409
402
  set stackTrace(stackTrace: StackTrace.StackTrace.StackTrace) {
403
+ if (this.#stackTrace) {
404
+ this.#stackTrace.removeEventListener(StackTrace.StackTrace.Events.UPDATED, this.requestUpdate, this);
405
+ }
410
406
  this.#stackTrace = stackTrace;
407
+ this.#stackTrace.addEventListener(StackTrace.StackTrace.Events.UPDATED, this.requestUpdate, this);
411
408
  this.requestUpdate();
412
409
  }
413
410
 
@@ -345,8 +345,7 @@ export class Linkifier extends Common.ObjectWrapper.ObjectWrapper<EventTypes> im
345
345
  }
346
346
 
347
347
  maybeLinkifyStackTraceFrame(
348
- target: SDK.Target.Target|null, frame: StackTrace.StackTrace.Frame, options?: LinkifyOptions): HTMLElement|null {
349
- let fallbackAnchor: HTMLElement|null = null;
348
+ target: SDK.Target.Target|null, frame: StackTrace.StackTrace.Frame, options?: LinkifyOptions): HTMLElement {
350
349
  const linkifyURLOptions: LinkifyURLOptions = {
351
350
  ...options,
352
351
  lineNumber: frame.line,
@@ -361,10 +360,8 @@ export class Linkifier extends Common.ObjectWrapper.ObjectWrapper<EventTypes> im
361
360
  omitOrigin: options?.omitOrigin,
362
361
  };
363
362
  const {className = ''} = linkifyURLOptions;
364
- if (frame.url) {
365
- fallbackAnchor = Linkifier.linkifyURL(frame.url as Platform.DevToolsPath.UrlString, linkifyURLOptions);
366
- }
367
- if (!target || target.isDisposed()) {
363
+ const fallbackAnchor = Linkifier.linkifyURL(frame.url as Platform.DevToolsPath.UrlString, linkifyURLOptions);
364
+ if (!target || target.isDisposed() || !frame.uiSourceCode) {
368
365
  return fallbackAnchor;
369
366
  }
370
367
 
@@ -383,7 +380,7 @@ export class Linkifier extends Common.ObjectWrapper.ObjectWrapper<EventTypes> im
383
380
  revealBreakpoint: options?.revealBreakpoint,
384
381
  };
385
382
 
386
- const uiLocation = frame.uiSourceCode?.uiLocation(frame.line, frame.column) ?? null;
383
+ const uiLocation = frame.uiSourceCode.uiLocation(frame.line, frame.column) ?? null;
387
384
  this.updateAnchorFromUILocation(link, linkDisplayOptions, uiLocation);
388
385
 
389
386
  const anchors = (this.anchorsByTarget.get(target) as Element[]);
@@ -923,12 +923,14 @@ export const knownContextValues = new Set([
923
923
  'content-type',
924
924
  'content-visibility',
925
925
  'context',
926
+ 'context-added',
926
927
  'context-change-drjones-file',
927
928
  'context-change-drjones-network-request',
928
929
  'context-change-drjones-performance',
929
930
  'context-change-freestyler',
930
931
  'context-change-none',
931
932
  'context-change-performance-insight',
933
+ 'context-removed',
932
934
  'context3',
933
935
  'contextmenu',
934
936
  'continue',
@@ -7,6 +7,13 @@ import commonStyle from './common.css';
7
7
  import {adoptStyleSheet} from './common.js';
8
8
  import {gridStyle} from './highlight_grid_common.js';
9
9
  // @ts-expect-error Importing CSS is handled in Rollup.
10
+ import greenDevAnchorsStyle from './tool_green_dev_anchors.css';
11
+ import {
12
+ type GreenDevAnchorsDispatchMessage,
13
+ GreenDevAnchorsOverlay,
14
+ type GreenDevAnchorsToolMessage
15
+ } from './tool_green_dev_anchors.js';
16
+ // @ts-expect-error Importing CSS is handled in Rollup.
10
17
  import highlightGridStyle from './tool_grid.css';
11
18
  // @ts-expect-error Importing CSS is handled in Rollup.
12
19
  import highlightStyle from './tool_highlight.css';
@@ -29,7 +36,9 @@ import {WindowControlsOverlay} from './tool_window_controls.js';
29
36
  declare global {
30
37
  interface Window {
31
38
  // eslint-disable-next-line @typescript-eslint/naming-convention
32
- InspectorOverlayHost: {send(data: PausedToolMessage|PersistentToolMessage|ScreenshotToolMessage|string): void};
39
+ InspectorOverlayHost: {
40
+ send(data: PausedToolMessage|PersistentToolMessage|ScreenshotToolMessage|GreenDevAnchorsToolMessage|string): void,
41
+ };
33
42
  }
34
43
  }
35
44
 
@@ -39,14 +48,18 @@ const gridStyleSheet = new CSSStyleSheet();
39
48
  gridStyleSheet.replaceSync(gridStyle);
40
49
 
41
50
  const highlightOverlay = new HighlightOverlay(window, [highlightStyle, gridStyleSheet]);
42
- const persistentOverlay = new PersistentOverlay(window, [highlightGridStyle, gridStyleSheet]);
51
+ const persistentOverlay = new PersistentOverlay(window, [highlightGridStyle, greenDevAnchorsStyle, gridStyleSheet]);
43
52
  const pausedOverlay = new PausedOverlay(window, pausedStyle);
44
53
  const screenshotOverlay = new ScreenshotOverlay(window, screenshotStyle);
54
+ const greenDevAnchorsOverlay = new GreenDevAnchorsOverlay(window, greenDevAnchorsStyle);
45
55
  const sourceOrderOverlay = new SourceOrderOverlay(window, sourceOrderStyle);
46
56
  const viewportSizeOverlay = new ViewportSizeOverlay(window);
47
57
  const windowControlsOverlay = new WindowControlsOverlay(window, [wcoStyle]);
48
58
 
59
+ persistentOverlay.setGreenDevAnchorsOverlay(greenDevAnchorsOverlay);
60
+
49
61
  interface Overlays {
62
+ greenDevFloaty: GreenDevAnchorsOverlay;
50
63
  highlight: HighlightOverlay;
51
64
  persistent: PersistentOverlay;
52
65
  paused: PausedOverlay;
@@ -60,6 +73,7 @@ type PlatformName = string;
60
73
 
61
74
  // Key in this object is the name the backend refers to a particular overlay by.
62
75
  const overlays: Overlays = {
76
+ greenDevFloaty: greenDevAnchorsOverlay,
63
77
  highlight: highlightOverlay,
64
78
  persistent: persistentOverlay,
65
79
  paused: pausedOverlay,
@@ -76,6 +90,7 @@ interface MessageLookup {
76
90
  setOverlay: keyof Overlays;
77
91
  setPlatform: PlatformName;
78
92
  drawingFinished: '';
93
+ update: GreenDevAnchorsDispatchMessage;
79
94
  }
80
95
 
81
96
  const dispatch = <K extends keyof MessageLookup>(message: [a: K, b: MessageLookup[K]]) => {
@@ -93,7 +108,7 @@ const dispatch = <K extends keyof MessageLookup>(message: [a: K, b: MessageLooku
93
108
  currentOverlay.install();
94
109
  }
95
110
  } else if (functionName === 'setPlatform') {
96
- platformName = message[1];
111
+ platformName = message[1] as PlatformName;
97
112
  } else if (functionName === 'drawingFinished') {
98
113
  // TODO The logic needs to be added here once the backend starts sending this event.
99
114
  } else {
@@ -0,0 +1,54 @@
1
+ /*
2
+ * Copyright 2026 The Chromium Authors
3
+ * Use of this source code is governed by a BSD-style license that can be
4
+ * found in the LICENSE file.
5
+ */
6
+ /* eslint-disable color-named, plugin/use_theme_colors */
7
+
8
+ :root {
9
+ --gemini-logo: url("data:image/svg+xml,%0A%3Csvg%20fill%3D%22none%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewBox%3D%220%200%2065%2065%22%3E%3Cmask%20id%3D%22maskme%22%20style%3D%22mask-type%3Aalpha%22%20maskUnits%3D%22userSpaceOnUse%22%20x%3D%220%22%20y%3D%220%22%20width%3D%2265%22%20height%3D%2265%22%3E%3Cpath%20d%3D%22M32.447%200c.68%200%201.273.465%201.439%201.125a38.904%2038.904%200%20001.999%205.905c2.152%205%205.105%209.376%208.854%2013.125%203.751%203.75%208.126%206.703%2013.125%208.855a38.98%2038.98%200%20005.906%201.999c.66.166%201.124.758%201.124%201.438%200%20.68-.464%201.273-1.125%201.439a38.902%2038.902%200%2000-5.905%201.999c-5%202.152-9.375%205.105-13.125%208.854-3.749%203.751-6.702%208.126-8.854%2013.125a38.973%2038.973%200%2000-2%205.906%201.485%201.485%200%2001-1.438%201.124c-.68%200-1.272-.464-1.438-1.125a38.913%2038.913%200%2000-2-5.905c-2.151-5-5.103-9.375-8.854-13.125-3.75-3.749-8.125-6.702-13.125-8.854a38.973%2038.973%200%2000-5.905-2A1.485%201.485%200%20010%2032.448c0-.68.465-1.272%201.125-1.438a38.903%2038.903%200%20005.905-2c5-2.151%209.376-5.104%2013.125-8.854%203.75-3.749%206.703-8.125%208.855-13.125a38.972%2038.972%200%20001.999-5.905A1.485%201.485%200%200132.447%200z%22%20fill%3D%22%23000%22/%3E%3Cpath%20d%3D%22M32.447%200c.68%200%201.273.465%201.439%201.125a38.904%2038.904%200%20001.999%205.905c2.152%205%205.105%209.376%208.854%2013.125%203.751%203.75%208.126%206.703%2013.125%208.855a38.98%2038.98%200%20005.906%201.999c.66.166%201.124.758%201.124%201.438%200%20.68-.464%201.273-1.125%201.439a38.902%2038.902%200%2000-5.905%201.999c-5%202.152-9.375%205.105-13.125%208.854-3.749%203.751-6.702%208.126-8.854%2013.125a38.973%2038.973%200%2000-2%205.906%201.485%201.485%200%2001-1.438%201.124c-.68%200-1.272-.464-1.438-1.125a38.913%2038.913%200%2000-2-5.905c-2.151-5-5.103-9.375-8.854-13.125-3.75-3.749-8.125-6.702-13.125-8.854a38.973%2038.973%200%2000-5.905-2A1.485%201.485%200%20010%2032.448c0-.68.465-1.272%201.125-1.438a38.903%2038.903%200%20005.905-2c5-2.151%209.376-5.104%2013.125-8.854%203.75-3.749%206.703-8.125%208.855-13.125a38.972%2038.972%200%20001.999-5.905A1.485%201.485%200%200132.447%200z%22%20fill%3D%22%23000%22/%3E%3C/mask%3E%3Cg%20mask%3D%22url(%23maskme)%22%3E%3Cpath%20fill%3D%22%234285F4%22%20d%3D%22M0%200h38.235L65%2027.66%2065%2065H0z%22/%3E%3Cpath%20fill%3D%22%23EA4335%22%20d%3D%22M26.765%200H65v38.235z%22/%3E%3Cpath%20fill%3D%22%2334A853%22%20d%3D%22M0%2026.765H38.235V65H0z%22/%3E%3Cpath%20fill%3D%22%23FBBC04%22%20d%3D%22M27.66%2065L65%2038.235V65z%22/%3E%3C/g%3E%3C/svg%3E");
10
+ --open-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23666' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6' /%3E%3Cpolyline points='15 3 21 3 21 9' /%3E%3Cline x1='10' y1='14' x2='21' y2='3' /%3E%3C/svg%3E");
11
+ }
12
+
13
+ #green-dev-anchors-container {
14
+ position: absolute;
15
+ top: 0;
16
+ left: 0;
17
+ width: 100%;
18
+ height: 100%;
19
+ z-index: 1000;
20
+ pointer-events: none;
21
+ }
22
+
23
+ .green-dev-anchor-minimal {
24
+ position: absolute;
25
+ width: 70px;
26
+ height: 40px;
27
+ background-color: #fff;
28
+ border-radius: 12px;
29
+ box-shadow: 0 4px 8px rgb(0 0 0 / 20%);
30
+ pointer-events: auto;
31
+ z-index: 1001;
32
+ display: flex;
33
+ align-items: center;
34
+ justify-content: center;
35
+ padding: 0 5px;
36
+ cursor: pointer;
37
+ }
38
+
39
+ .green-dev-anchor-icon-gemini {
40
+ width: 24px;
41
+ height: 24px;
42
+ background-image: var(--gemini-logo);
43
+ background-size: cover;
44
+ background-repeat: no-repeat;
45
+ margin-right: 5px;
46
+ }
47
+
48
+ .green-dev-anchor-icon-open {
49
+ width: 20px;
50
+ height: 20px;
51
+ background-image: var(--open-icon);
52
+ background-size: cover;
53
+ background-repeat: no-repeat;
54
+ }
@@ -0,0 +1,164 @@
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 {Overlay} from './common.js';
6
+
7
+ export interface GreenDevAnchorsHighlight {
8
+ nodeId: number;
9
+ x: number;
10
+ y: number;
11
+ }
12
+
13
+ export type GreenDevAnchorsToolMessage = {
14
+ highlightType: 'greenDevFloaty',
15
+ command: 'setInputValue',
16
+ nodeId: number,
17
+ value: string,
18
+ }|{
19
+ highlightType: 'greenDevFloaty',
20
+ command: 'debugMessage',
21
+ message: string,
22
+ }|{
23
+ highlightType: 'greenDevFloaty',
24
+ command: 'openDevTools',
25
+ nodeId: number,
26
+ }|{
27
+ highlightType: 'greenDevFloaty',
28
+ command: 'restoreFloaty',
29
+ nodeId: number,
30
+ };
31
+
32
+ function consoleLog(message: string) {
33
+ window.InspectorOverlayHost.send({highlightType: 'greenDevFloaty', command: 'debugMessage', message});
34
+ }
35
+
36
+ export interface GreenDevAnchorsDispatchMessage {
37
+ nodeId: number;
38
+ value: string;
39
+ }
40
+
41
+ export class GreenDevAnchorsOverlay extends Overlay {
42
+ #anchorsContainer!: HTMLElement;
43
+ #anchorsByNodeId = new Map<number, HTMLElement>();
44
+
45
+ override install() {
46
+ consoleLog('GreenDevAnchorsOverlay.install() called');
47
+
48
+ this.document.body.classList.add('fill');
49
+
50
+ const canvas = this.document.createElement('canvas');
51
+ canvas.id = 'canvas';
52
+ canvas.classList.add('fill');
53
+ canvas.style.pointerEvents = 'none';
54
+ this.document.body.append(canvas);
55
+
56
+ const anchorsContainer = this.document.createElement('div');
57
+ anchorsContainer.id = 'green-dev-anchors-container';
58
+ this.document.body.append(anchorsContainer);
59
+ this.#anchorsContainer = anchorsContainer;
60
+
61
+ this.setCanvas(canvas);
62
+ super.install();
63
+ }
64
+
65
+ override uninstall() {
66
+ consoleLog('GreenDevAnchorsOverlay.uninstall() called');
67
+
68
+ this.document.body.classList.remove('fill');
69
+ if (this.#anchorsContainer && this.#anchorsContainer.parentElement) {
70
+ this.#anchorsContainer.parentElement.removeChild(this.#anchorsContainer);
71
+ }
72
+ // Clear the map as the elements are removed from DOM.
73
+ this.#anchorsByNodeId.clear();
74
+ super.uninstall();
75
+ }
76
+
77
+ drawGreenDevAnchors(highlights: GreenDevAnchorsHighlight[]) {
78
+ if (this.#anchorsContainer && !this.#anchorsContainer.isConnected) {
79
+ // The container was removed from the DOM (likely by PersistentOverlay.uninstall cleaning up),
80
+ // but this overlay still thinks it's installed. We need to re-install to restore the DOM.
81
+ try {
82
+ this.uninstall();
83
+ } catch (e) {
84
+ console.error('Error during uninstall in drawGreenDevAnchors:', e);
85
+ }
86
+ this.install();
87
+ }
88
+
89
+ const newAnchorsByNodeId = new Map<number, HTMLElement>();
90
+
91
+ for (const highlight of highlights) {
92
+ const {x, y, nodeId} = highlight;
93
+ let anchor = this.#anchorsByNodeId.get(nodeId);
94
+
95
+ if (anchor) {
96
+ anchor.style.left = `${x}px`;
97
+ anchor.style.top = `${y}px`;
98
+ // Remove from the old map so only anchors that need to be deleted remain.
99
+ this.#anchorsByNodeId.delete(nodeId);
100
+ } else {
101
+ const newAnchor = this.drawGreenDevAnchor(highlight);
102
+ if (!newAnchor) {
103
+ continue;
104
+ }
105
+ anchor = newAnchor;
106
+ anchor.style.left = `${x}px`;
107
+ anchor.style.top = `${y}px`;
108
+ this.#anchorsContainer.append(anchor);
109
+ }
110
+ newAnchorsByNodeId.set(nodeId, anchor);
111
+ }
112
+
113
+ // Remove any anchors that were not present in the new highlights list.
114
+ for (const anchor of this.#anchorsByNodeId.values()) {
115
+ anchor.remove();
116
+ }
117
+
118
+ this.#anchorsByNodeId = newAnchorsByNodeId;
119
+ }
120
+
121
+ private drawGreenDevAnchor(highlight: GreenDevAnchorsHighlight): HTMLElement {
122
+ const {nodeId} = highlight;
123
+ const anchor = this.document.createElement('div');
124
+ anchor.classList.add('green-dev-anchor-minimal');
125
+
126
+ const geminiIcon = this.document.createElement('div');
127
+ geminiIcon.classList.add('green-dev-anchor-icon-gemini');
128
+ anchor.append(geminiIcon);
129
+
130
+ const openIcon = this.document.createElement('div');
131
+ openIcon.classList.add('green-dev-anchor-icon-open');
132
+ anchor.append(openIcon);
133
+
134
+ const onEvent = (event: MouseEvent) => {
135
+ event.stopPropagation();
136
+ event.preventDefault();
137
+ };
138
+
139
+ openIcon.addEventListener('mousedown', onEvent);
140
+ openIcon.addEventListener('mouseup', onEvent);
141
+ openIcon.addEventListener('mousemove', onEvent);
142
+ openIcon.addEventListener('mouseenter', onEvent);
143
+ openIcon.addEventListener('mouseleave', onEvent);
144
+ openIcon.addEventListener('click', (event: MouseEvent) => {
145
+ onEvent(event);
146
+ window.InspectorOverlayHost.send({highlightType: 'greenDevFloaty', command: 'openDevTools', nodeId});
147
+ });
148
+
149
+ anchor.addEventListener('mousedown', onEvent);
150
+ anchor.addEventListener('mouseup', onEvent);
151
+ anchor.addEventListener('mousemove', onEvent);
152
+ anchor.addEventListener('mouseenter', onEvent);
153
+ anchor.addEventListener('mouseleave', onEvent);
154
+ anchor.addEventListener('click', (event: MouseEvent) => {
155
+ if (event.target !== anchor && !anchor.contains(event.target as Node)) {
156
+ return;
157
+ }
158
+ onEvent(event);
159
+ window.InspectorOverlayHost.send({highlightType: 'greenDevFloaty', command: 'restoreFloaty', nodeId});
160
+ });
161
+
162
+ return anchor;
163
+ }
164
+ }
@@ -9,6 +9,7 @@ import {drawLayoutFlexContainerHighlight, type FlexContainerHighlight} from './h
9
9
  import {drawLayoutGridHighlight, type GridHighlight} from './highlight_grid_common.js';
10
10
  import {drawIsolatedElementHighlight, type IsolatedElementHighlight} from './highlight_isolated_element.js';
11
11
  import {drawScrollSnapHighlight, type ScrollSnapHighlight} from './highlight_scroll_snap.js';
12
+ import type {GreenDevAnchorsHighlight, GreenDevAnchorsOverlay} from './tool_green_dev_anchors.js';
12
13
 
13
14
  export interface PersistentToolMessage {
14
15
  highlightType: string;
@@ -65,11 +66,17 @@ export class PersistentOverlay extends Overlay {
65
66
  initialHeight: number,
66
67
  }>();
67
68
  private dragHandler?: DragResizeHandler;
69
+ private greenDevAnchorsOverlay?: GreenDevAnchorsOverlay;
70
+
71
+ setGreenDevAnchorsOverlay(greenDevAnchorsOverlay: GreenDevAnchorsOverlay) {
72
+ this.greenDevAnchorsOverlay = greenDevAnchorsOverlay;
73
+ }
68
74
 
69
75
  override reset(data: ResetData) {
70
76
  super.reset(data);
71
77
  this.gridLabelState.gridLayerCounter = 0;
72
78
  this.gridLabels.innerHTML = '';
79
+ this.greenDevAnchorsOverlay?.reset(data);
73
80
  }
74
81
 
75
82
  renderGridMarkup() {
@@ -129,6 +136,13 @@ export class PersistentOverlay extends Overlay {
129
136
  this.context.restore();
130
137
  }
131
138
 
139
+ drawGreenDevFloatyAnchors(highlights: GreenDevAnchorsHighlight[]) {
140
+ if (this.greenDevAnchorsOverlay && !this.greenDevAnchorsOverlay.installed) {
141
+ this.greenDevAnchorsOverlay.install();
142
+ }
143
+ this.greenDevAnchorsOverlay?.drawGreenDevAnchors(highlights);
144
+ }
145
+
132
146
  drawIsolatedElementHighlight(highlight: IsolatedElementHighlight) {
133
147
  if (!this.dragHandler) {
134
148
  this.dragHandler = new DragResizeHandler(this.document, makeDraggableDelegate(this));
package/package.json CHANGED
@@ -105,5 +105,5 @@
105
105
  "flat-cache": "6.1.12"
106
106
  }
107
107
  },
108
- "version": "1.0.1581449"
108
+ "version": "1.0.1582745"
109
109
  }