chrome-devtools-frontend 1.0.1536371 → 1.0.1537860

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 (231) hide show
  1. package/.env.template +9 -0
  2. package/docs/get_the_code.md +27 -0
  3. package/front_end/core/common/SettingRegistration.ts +10 -7
  4. package/front_end/core/common/Settings.ts +3 -0
  5. package/front_end/core/host/AidaClient.ts +64 -5
  6. package/front_end/core/host/DispatchHttpRequestClient.ts +62 -0
  7. package/front_end/core/host/GdpClient.ts +8 -57
  8. package/front_end/core/host/host.ts +2 -0
  9. package/front_end/core/protocol_client/CDPConnection.ts +10 -8
  10. package/front_end/core/protocol_client/InspectorBackend.ts +36 -42
  11. package/front_end/core/sdk/EnhancedTracesParser.ts +20 -5
  12. package/front_end/core/sdk/RehydratingConnection.ts +112 -4
  13. package/front_end/core/sdk/RehydratingObject.ts +8 -0
  14. package/front_end/core/sdk/TraceObject.ts +5 -1
  15. package/front_end/core/sdk/sdk-meta.ts +8 -2
  16. package/front_end/entrypoints/inspector_main/RenderingOptions.ts +4 -3
  17. package/front_end/generated/SupportedCSSProperties.js +1 -0
  18. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +23 -7
  19. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +110 -5
  20. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +50 -45
  21. package/front_end/models/cpu_profile/ProfileTreeModel.ts +7 -7
  22. package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
  23. package/front_end/models/trace/types/File.ts +9 -0
  24. package/front_end/models/trace_source_maps_resolver/SourceMapsResolver.ts +1 -1
  25. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +5 -9
  26. package/front_end/panels/ai_assistance/components/ChatView.ts +58 -70
  27. package/front_end/panels/application/BackForwardCacheTreeElement.ts +2 -6
  28. package/front_end/panels/application/StorageView.ts +3 -2
  29. package/front_end/panels/application/components/BackForwardCacheView.ts +96 -108
  30. package/front_end/panels/application/components/FrameDetailsView.ts +8 -11
  31. package/front_end/panels/application/components/OriginTrialTreeView.ts +136 -137
  32. package/front_end/panels/application/components/backForwardCacheView.css +8 -0
  33. package/front_end/panels/application/components/badge.css +9 -1
  34. package/front_end/panels/application/preloading/components/PreloadingGrid.ts +2 -2
  35. package/front_end/panels/application/preloading/components/PreloadingString.ts +27 -0
  36. package/front_end/panels/autofill/AutofillView.ts +1 -1
  37. package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +44 -53
  38. package/front_end/panels/console/ConsoleView.ts +11 -9
  39. package/front_end/panels/coverage/CoverageView.ts +1 -2
  40. package/front_end/panels/css_overview/CSSOverviewSidebarPanel.ts +1 -1
  41. package/front_end/panels/developer_resources/DeveloperResourcesView.ts +1 -1
  42. package/front_end/panels/elements/ElementStatePaneWidget.ts +1 -1
  43. package/front_end/panels/elements/EventListenersWidget.ts +1 -2
  44. package/front_end/panels/elements/PropertiesWidget.ts +1 -1
  45. package/front_end/panels/network/NetworkConfigView.ts +2 -1
  46. package/front_end/panels/network/NetworkPanel.ts +5 -4
  47. package/front_end/panels/network/RequestCookiesView.ts +2 -1
  48. package/front_end/panels/profiler/HeapSnapshotView.ts +3 -2
  49. package/front_end/panels/recorder/RecorderController.ts +1 -2
  50. package/front_end/panels/recorder/components/CreateRecordingView.ts +153 -129
  51. package/front_end/panels/sensors/SensorsView.ts +4 -3
  52. package/front_end/panels/settings/AISettingsTab.ts +162 -171
  53. package/front_end/panels/settings/FrameworkIgnoreListSettingsTab.ts +8 -6
  54. package/front_end/panels/settings/KeybindsSettingsTab.ts +3 -2
  55. package/front_end/panels/settings/SettingsScreen.ts +5 -8
  56. package/front_end/panels/settings/WorkspaceSettingsTab.ts +1 -1
  57. package/front_end/panels/settings/aiSettingsTab.css +151 -148
  58. package/front_end/panels/settings/settings-meta.ts +1 -2
  59. package/front_end/panels/sources/AddSourceMapURLDialog.ts +23 -26
  60. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +2 -1
  61. package/front_end/panels/sources/SourcesPanel.ts +2 -1
  62. package/front_end/panels/sources/sources-meta.ts +8 -1
  63. package/front_end/panels/timeline/TimelinePanel.ts +64 -14
  64. package/front_end/panels/timeline/TimelineUIUtils.ts +4 -20
  65. package/front_end/panels/timeline/components/ExportTraceOptions.ts +33 -34
  66. package/front_end/panels/timeline/components/LiveMetricsView.ts +1 -0
  67. package/front_end/panels/timeline/components/SidebarAnnotationsTab.ts +2 -0
  68. package/front_end/third_party/chromium/README.chromium +2 -2
  69. package/front_end/third_party/puppeteer/README.chromium +2 -2
  70. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Browser.d.ts +9 -1
  71. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Browser.d.ts.map +1 -1
  72. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Browser.js.map +1 -1
  73. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/BrowserContext.d.ts +2 -2
  74. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/BrowserContext.d.ts.map +1 -1
  75. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/BrowserContext.js.map +1 -1
  76. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts +13 -1
  77. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts.map +1 -1
  78. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js.map +1 -1
  79. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/BrowserContext.d.ts +2 -2
  80. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/BrowserContext.d.ts.map +1 -1
  81. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/BrowserContext.js +5 -2
  82. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/BrowserContext.js.map +1 -1
  83. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts +2 -2
  84. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts.map +1 -1
  85. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js +3 -1
  86. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js.map +1 -1
  87. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +1 -12
  88. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts.map +1 -1
  89. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Accessibility.d.ts +6 -0
  90. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Accessibility.d.ts.map +1 -1
  91. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Accessibility.js +1 -0
  92. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Accessibility.js.map +1 -1
  93. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts +2 -2
  94. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts.map +1 -1
  95. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js +6 -1
  96. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js.map +1 -1
  97. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/BrowserContext.d.ts +2 -1
  98. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/BrowserContext.d.ts.map +1 -1
  99. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/BrowserContext.js +2 -2
  100. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/BrowserContext.js.map +1 -1
  101. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts +2 -2
  102. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts.map +1 -1
  103. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js +3 -1
  104. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js.map +1 -1
  105. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  106. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
  107. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
  108. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  109. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  110. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/disposable.d.ts +2 -2
  111. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/disposable.d.ts.map +1 -1
  112. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/disposable.js +3 -1
  113. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/disposable.js.map +1 -1
  114. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/util.d.ts +1 -0
  115. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/util.d.ts.map +1 -1
  116. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/util.js +1 -0
  117. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/util.js.map +1 -1
  118. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  119. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  120. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +28 -3
  121. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +21 -10
  122. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.d.ts +9 -1
  123. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.d.ts.map +1 -1
  124. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Browser.js.map +1 -1
  125. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/BrowserContext.d.ts +2 -2
  126. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/BrowserContext.d.ts.map +1 -1
  127. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/BrowserContext.js.map +1 -1
  128. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Input.d.ts +1 -1
  129. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Input.d.ts.map +1 -1
  130. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts +13 -1
  131. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts.map +1 -1
  132. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js.map +1 -1
  133. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/BrowserContext.d.ts +2 -2
  134. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/BrowserContext.d.ts.map +1 -1
  135. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/BrowserContext.js +5 -2
  136. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/BrowserContext.js.map +1 -1
  137. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts +2 -2
  138. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts.map +1 -1
  139. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js +3 -1
  140. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js.map +1 -1
  141. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/Realm.d.ts +1 -12
  142. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/Realm.d.ts.map +1 -1
  143. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Accessibility.d.ts +6 -0
  144. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Accessibility.d.ts.map +1 -1
  145. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Accessibility.js +1 -0
  146. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Accessibility.js.map +1 -1
  147. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts +2 -2
  148. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts.map +1 -1
  149. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js +6 -1
  150. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js.map +1 -1
  151. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/BrowserContext.d.ts +2 -1
  152. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/BrowserContext.d.ts.map +1 -1
  153. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/BrowserContext.js +2 -2
  154. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/BrowserContext.js.map +1 -1
  155. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts +2 -2
  156. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts.map +1 -1
  157. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js +3 -1
  158. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js.map +1 -1
  159. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
  160. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
  161. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  162. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/disposable.d.ts +2 -2
  163. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/disposable.d.ts.map +1 -1
  164. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/disposable.js +2 -2
  165. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/disposable.js.map +1 -1
  166. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/util.d.ts +1 -0
  167. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/util.d.ts.map +1 -1
  168. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/util.js +1 -0
  169. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/util.js.map +1 -1
  170. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  171. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  172. package/front_end/third_party/puppeteer/package/lib/types.d.ts +28 -3
  173. package/front_end/third_party/puppeteer/package/package.json +2 -2
  174. package/front_end/third_party/puppeteer/package/src/api/Browser.ts +13 -1
  175. package/front_end/third_party/puppeteer/package/src/api/BrowserContext.ts +7 -2
  176. package/front_end/third_party/puppeteer/package/src/api/Page.ts +14 -1
  177. package/front_end/third_party/puppeteer/package/src/bidi/BrowserContext.ts +8 -5
  178. package/front_end/third_party/puppeteer/package/src/bidi/Page.ts +5 -2
  179. package/front_end/third_party/puppeteer/package/src/cdp/Accessibility.ts +8 -0
  180. package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +11 -2
  181. package/front_end/third_party/puppeteer/package/src/cdp/BrowserContext.ts +3 -2
  182. package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +5 -5
  183. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  184. package/front_end/third_party/puppeteer/package/src/util/disposable.ts +2 -2
  185. package/front_end/third_party/puppeteer/package/src/util/util.ts +1 -0
  186. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  187. package/front_end/ui/components/adorners/Adorner.ts +2 -1
  188. package/front_end/ui/components/buttons/Button.docs.ts +195 -0
  189. package/front_end/ui/components/settings/SettingCheckbox.ts +49 -14
  190. package/front_end/ui/components/settings/settingCheckbox.css +6 -1
  191. package/front_end/ui/components/spinners/Spinners.docs.ts +13 -0
  192. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +280 -0
  193. package/front_end/ui/components/text_editor/text_editor.ts +1 -0
  194. package/front_end/ui/components/tooltips/Tooltip.docs.ts +76 -0
  195. package/front_end/ui/components/tooltips/Tooltip.ts +1 -1
  196. package/front_end/ui/legacy/Dialog.ts +0 -1
  197. package/front_end/ui/legacy/FilterBar.ts +1 -2
  198. package/front_end/ui/legacy/RadioButton.docs.ts +41 -0
  199. package/front_end/ui/legacy/SelectMenu.docs.ts +98 -0
  200. package/front_end/ui/legacy/Toolbar.ts +4 -6
  201. package/front_end/ui/legacy/UIUtils.ts +114 -1
  202. package/front_end/ui/legacy/Widget.ts +62 -34
  203. package/front_end/ui/legacy/XLink.ts +0 -3
  204. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +9 -0
  205. package/front_end/ui/legacy/components/settings_ui/SettingsUI.ts +125 -0
  206. package/front_end/ui/legacy/components/settings_ui/settings_ui.ts +8 -0
  207. package/front_end/ui/legacy/components/utils/Linkifier.ts +9 -3
  208. package/front_end/ui/legacy/legacy.ts +0 -2
  209. package/front_end/ui/visual_logging/KnownContextValues.ts +4 -1
  210. package/mcp/mcp.ts +5 -0
  211. package/package.json +1 -1
  212. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatterBounds.snapshot.txt +0 -4
  213. package/front_end/models/trace/lantern/testing/MetricTestUtils.ts +0 -62
  214. package/front_end/models/trace/lantern/testing/testing.ts +0 -5
  215. package/front_end/ui/components/docs/breadcrumbs_perf/initial-breadcrumb-perf.html +0 -20
  216. package/front_end/ui/components/docs/breadcrumbs_perf/initial-breadcrumb-perf.ts +0 -25
  217. package/front_end/ui/components/docs/breadcrumbs_perf/nested-breadcrumbs-perf.html +0 -20
  218. package/front_end/ui/components/docs/breadcrumbs_perf/nested-breadcrumbs-perf.ts +0 -36
  219. package/front_end/ui/components/docs/button/basic.html +0 -44
  220. package/front_end/ui/components/docs/button/basic.ts +0 -175
  221. package/front_end/ui/components/docs/radio_button/basic.html +0 -23
  222. package/front_end/ui/components/docs/radio_button/basic.ts +0 -50
  223. package/front_end/ui/components/docs/select_menu/basic.html +0 -19
  224. package/front_end/ui/components/docs/select_menu/basic.ts +0 -95
  225. package/front_end/ui/components/docs/select_menu/wide-option.html +0 -38
  226. package/front_end/ui/components/docs/select_menu/wide-option.ts +0 -43
  227. package/front_end/ui/components/docs/spinners/basic.html +0 -17
  228. package/front_end/ui/components/docs/spinners/basic.ts +0 -22
  229. package/front_end/ui/components/docs/tooltip/basic.html +0 -20
  230. package/front_end/ui/components/docs/tooltip/basic.ts +0 -82
  231. package/front_end/ui/legacy/SettingsUI.ts +0 -254
@@ -0,0 +1,76 @@
1
+ // Copyright 2025 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 Lit from '../../lit/lit.js';
6
+
7
+ import {Tooltip} from './Tooltip.js';
8
+
9
+ const {html} = Lit;
10
+
11
+ export async function render(container: HTMLElement) {
12
+ Lit.render(
13
+ html`
14
+ <div style="position: relative; z-index: 0;">
15
+ <button aria-describedby="simple-tooltip" style="position: absolute; left: 16px; top: 16px;">
16
+ Simple
17
+ </button>
18
+ <devtools-tooltip id="simple-tooltip">Simple content</devtools-tooltip>
19
+ </div>
20
+ <div style="position: relative; z-index: 0;">
21
+ <span
22
+ aria-details="rich-tooltip"
23
+ style="position: absolute; left: 16px; top: 116px; border: 1px solid black;"
24
+ >
25
+ Non-button click trigger
26
+ </span>
27
+ <devtools-tooltip id="rich-tooltip" variant="rich" use-click>
28
+ <p>Rich tooltip</p>
29
+ <button>Action</button>
30
+ </devtools-tooltip>
31
+ </div>
32
+ <div style="position: relative; z-index: 0;">
33
+ <button
34
+ id="removable"
35
+ @click=${() => document.getElementById('removable')?.remove()}
36
+ class="anchor"
37
+ aria-details="programatic"
38
+ style="position: absolute; left: 16px; top: 216px;"
39
+ >
40
+ Click to remove anchor
41
+ </button>
42
+ </div>
43
+ `,
44
+ container);
45
+
46
+ const anchor = container.querySelector('.anchor') as HTMLElement;
47
+ const programmaticTooltip = new Tooltip({id: 'programatic', variant: 'rich', anchor});
48
+ programmaticTooltip.append('Text content');
49
+ anchor.insertAdjacentElement('afterend', programmaticTooltip);
50
+
51
+ // Make the buttons draggable, so that we can experiment with the position of the tooltip.
52
+ container.querySelectorAll('button,span').forEach(anchor => draggable(anchor as HTMLElement));
53
+ function draggable(element: HTMLElement|null): void {
54
+ if (!element) {
55
+ return;
56
+ }
57
+ element.addEventListener('mousedown', event => {
58
+ const target = event.target as HTMLElement;
59
+ const offsetX = event.clientX - target.getBoundingClientRect().left + container.getBoundingClientRect().left;
60
+ const offsetY = event.clientY - target.getBoundingClientRect().top + container.getBoundingClientRect().top;
61
+
62
+ function onMouseMove(event: MouseEvent) {
63
+ target.style.left = `${event.clientX - offsetX}px`;
64
+ target.style.top = `${event.clientY - offsetY}px`;
65
+ }
66
+
67
+ function onMouseUp() {
68
+ document.removeEventListener('mousemove', onMouseMove);
69
+ document.removeEventListener('mouseup', onMouseUp);
70
+ }
71
+
72
+ document.addEventListener('mousemove', onMouseMove);
73
+ document.addEventListener('mouseup', onMouseUp);
74
+ });
75
+ }
76
+ }
@@ -443,7 +443,7 @@ export class Tooltip extends HTMLElement {
443
443
  this.#previousAnchorRect = anchorRect;
444
444
  this.#previousPopoverRect = currentPopoverRect;
445
445
 
446
- const inspectorViewRect = UI.InspectorView.InspectorView.instance().element.getBoundingClientRect();
446
+ const inspectorViewRect = UI.UIUtils.getDevToolsBoundingElement().getBoundingClientRect();
447
447
  const preferredPositions =
448
448
  this.preferSpanLeft ? [PositionOption.BOTTOM_SPAN_LEFT, PositionOption.TOP_SPAN_LEFT] : [];
449
449
  const proposedPopoverRect = this.variant === 'rich' ?
@@ -33,7 +33,6 @@ export class Dialog extends Common.ObjectWrapper.eventMixin<EventTypes, typeof G
33
33
  this.contentElement.setAttribute(
34
34
  'jslog', `${VisualLogging.dialog(jslogContext).track({resize: true, keydown: 'Escape'})}`);
35
35
  }
36
- this.widget().setDefaultFocusedElement(this.contentElement);
37
36
  this.setPointerEventsBehavior(PointerEventsBehavior.BLOCKED_BY_GLASS_PANE);
38
37
  this.setOutsideClickCallback(event => {
39
38
  // If there are stacked dialogs, we only want to
@@ -15,11 +15,10 @@ import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
15
15
  import * as ARIAUtils from './ARIAUtils.js';
16
16
  import filterStyles from './filter.css.js';
17
17
  import {KeyboardShortcut, Modifiers} from './KeyboardShortcut.js';
18
- import {bindCheckbox} from './SettingsUI.js';
19
18
  import type {Suggestions} from './SuggestBox.js';
20
19
  import {type ToolbarButton, ToolbarFilter, ToolbarInput, ToolbarSettingToggle} from './Toolbar.js';
21
20
  import {Tooltip} from './Tooltip.js';
22
- import {CheckboxLabel, createTextChild} from './UIUtils.js';
21
+ import {bindCheckbox, CheckboxLabel, createTextChild} from './UIUtils.js';
23
22
  import {HBox} from './Widget.js';
24
23
 
25
24
  const UIStrings = {
@@ -0,0 +1,41 @@
1
+ // Copyright 2024 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 {UIUtils} from './legacy.js';
6
+
7
+ export async function render(container: HTMLElement) {
8
+ const styleElement = document.createElement('style');
9
+ styleElement.textContent = 'fieldset { label { display: block; } }';
10
+ container.appendChild(styleElement);
11
+
12
+ function radioExample({name, tabbable, disabled}: {
13
+ name: string,
14
+ tabbable: boolean,
15
+ disabled: boolean,
16
+ }): HTMLElement {
17
+ const example = document.createElement('fieldset');
18
+ example.style.marginTop = '20px';
19
+ const legend = document.createElement('legend');
20
+ legend.textContent = name;
21
+ const list = document.createElement('div');
22
+ for (let i = 0; i < 3; ++i) {
23
+ const {label, radio} = UIUtils.createRadioButton(name, `Option #${i + 1}`, name);
24
+ radio.tabIndex = tabbable ? 0 : -1;
25
+ radio.disabled = disabled;
26
+ radio.checked = i === 0;
27
+ list.append(label);
28
+ }
29
+ example.append(legend, list);
30
+ return example;
31
+ }
32
+
33
+ // Basic
34
+ container.appendChild(radioExample({name: 'basic', tabbable: true, disabled: false}));
35
+
36
+ // Not tab reachable
37
+ container.appendChild(radioExample({name: 'not-table-reachable', tabbable: false, disabled: false}));
38
+
39
+ // Disabled
40
+ container.appendChild(radioExample({name: 'disabled', tabbable: true, disabled: true}));
41
+ }
@@ -0,0 +1,98 @@
1
+ // Copyright 2023 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 Lit from '../lit/lit.js';
6
+ import * as VisualLogging from '../visual_logging/visual_logging.js';
7
+
8
+ import {UIUtils} from './legacy.js';
9
+
10
+ const {html} = Lit;
11
+
12
+ export async function render(container: HTMLElement) {
13
+ function createDivWithP(text: string): HTMLDivElement {
14
+ const div = document.createElement('div');
15
+ div.style.paddingLeft = '25px';
16
+ const p = document.createElement('p');
17
+ p.style.marginLeft = '-25px';
18
+ p.textContent = text;
19
+ div.appendChild(p);
20
+ container.appendChild(div);
21
+ return div;
22
+ }
23
+
24
+ function onChange(event: Event): void {
25
+ const menu = event.target;
26
+ if (menu instanceof HTMLSelectElement) {
27
+ // eslint-disable-next-line no-console
28
+ console.log('Option selected: ', menu.value);
29
+ }
30
+ }
31
+
32
+ {
33
+ const simpleMenuHTML = createDivWithP('Simple item select with lit-html');
34
+ Lit.render(
35
+ html`<select id="menu" aria-label="Select an option"
36
+ @change=${onChange}>
37
+ <option hidden>Select an option</option>
38
+ <option id="option-1" jslog=${VisualLogging.item('option-1').track({
39
+ click: true
40
+ })}
41
+ value="Option1">Option 1</option>
42
+ <option jslog=${VisualLogging.item('option-2').track({
43
+ click: true
44
+ })}
45
+ value="Option2">Option 2</option>
46
+ <option disabled jslog=${VisualLogging.item('option-3').track({
47
+ click: true
48
+ })}
49
+ value="Option3">Option 3</option>
50
+ </select>`,
51
+ simpleMenuHTML);
52
+ }
53
+
54
+ {
55
+ const groupMenuHTML = createDivWithP('Select with groups with lit-html');
56
+ Lit.render(
57
+ html`<select aria-label="Select an option"
58
+ @change=${onChange}>
59
+ <optgroup label="Group 1">
60
+ <option jslog=${VisualLogging.item('option-1').track({
61
+ click: true
62
+ })}
63
+ value="Option1">Option 1</option>
64
+ </optgroup>
65
+ <optgroup label="Group 2">
66
+ <option jslog=${VisualLogging.item('option-2').track({
67
+ click: true
68
+ })}
69
+ value="Option2">Option 2</option>
70
+ <option jslog=${VisualLogging.item('option-3').track({
71
+ click: true
72
+ })}
73
+ value="Option3">Option 3</option>
74
+ </optgroup>
75
+ </select>`,
76
+ groupMenuHTML);
77
+ }
78
+
79
+ {
80
+ const simpleMenuImperative = createDivWithP('Simple item select with imperative API');
81
+ const simpleSelect = UIUtils.createSelect('Select an option', [
82
+ 'Option 1',
83
+ 'Option 2',
84
+ 'Option 3',
85
+ ]);
86
+ simpleSelect.addEventListener('change', event => onChange(event));
87
+ simpleMenuImperative.appendChild(simpleSelect);
88
+ }
89
+
90
+ {
91
+ const groupMenuImperative = createDivWithP('Select with groups with imperative API');
92
+ const group1 = new Map<string, string[]>([['Group 1', ['Option 1']]]);
93
+ const group2 = new Map<string, string[]>([['Group 2', ['Option 2', 'Option 3']]]);
94
+ const groupSelect = UIUtils.createSelect('Select an option', [group1, group2]);
95
+ groupSelect.addEventListener('change', event => onChange(event));
96
+ groupMenuImperative.appendChild(groupSelect);
97
+ }
98
+ }
@@ -10,7 +10,6 @@ import * as Platform from '../../core/platform/platform.js';
10
10
  import * as Root from '../../core/root/root.js';
11
11
  import * as Buttons from '../../ui/components/buttons/buttons.js';
12
12
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
13
- import type * as Adorners from '../components/adorners/adorners.js';
14
13
  import * as IconButton from '../components/icon_button/icon_button.js';
15
14
 
16
15
  import {type Action, Events as ActionEvents} from './ActionRegistration.js';
@@ -18,12 +17,11 @@ import {ActionRegistry} from './ActionRegistry.js';
18
17
  import * as ARIAUtils from './ARIAUtils.js';
19
18
  import {ContextMenu} from './ContextMenu.js';
20
19
  import {GlassPane, PointerEventsBehavior} from './GlassPane.js';
21
- import {bindCheckbox} from './SettingsUI.js';
22
20
  import type {Suggestion} from './SuggestBox.js';
23
21
  import {Events as TextPromptEvents, TextPrompt} from './TextPrompt.js';
24
22
  import toolbarStyles from './toolbar.css.js';
25
23
  import {Tooltip} from './Tooltip.js';
26
- import {CheckboxLabel, LongClickController} from './UIUtils.js';
24
+ import {bindCheckbox, CheckboxLabel, LongClickController} from './UIUtils.js';
27
25
 
28
26
  const UIStrings = {
29
27
  /**
@@ -643,7 +641,7 @@ export class ToolbarButton extends ToolbarItem<ToolbarButton.EventTypes, Buttons
643
641
  this.text = text;
644
642
  }
645
643
 
646
- setAdorner(adorner: Adorners.Adorner.Adorner): void {
644
+ setAdorner(adorner: HTMLElement): void {
647
645
  if (this.adorner) {
648
646
  this.adorner.replaceWith(adorner);
649
647
  } else {
@@ -971,7 +969,7 @@ export class ToolbarMenuButton extends ToolbarItem<ToolbarButton.EventTypes> {
971
969
  private textElement?: HTMLElement;
972
970
  private text?: string;
973
971
  private iconName?: string;
974
- private adorner?: Adorners.Adorner.Adorner;
972
+ private adorner?: HTMLElement;
975
973
  private readonly contextMenuHandler: (arg0: ContextMenu) => void;
976
974
  private readonly useSoftMenu: boolean;
977
975
  private readonly keepOpen: boolean;
@@ -1028,7 +1026,7 @@ export class ToolbarMenuButton extends ToolbarItem<ToolbarButton.EventTypes> {
1028
1026
  this.text = text;
1029
1027
  }
1030
1028
 
1031
- setAdorner(adorner: Adorners.Adorner.Adorner): void {
1029
+ setAdorner(adorner: HTMLElement): void {
1032
1030
  if (this.iconName) {
1033
1031
  return;
1034
1032
  }
@@ -36,7 +36,7 @@
36
36
 
37
37
  import './Toolbar.js';
38
38
 
39
- import type * as Common from '../../core/common/common.js';
39
+ import * as Common from '../../core/common/common.js';
40
40
  import * as Host from '../../core/host/host.js';
41
41
  import * as i18n from '../../core/i18n/i18n.js';
42
42
  import * as Platform from '../../core/platform/platform.js';
@@ -2353,3 +2353,116 @@ export function copyTextToClipboard(text: string, alert?: string): void {
2353
2353
  export function getDevToolsBoundingElement(): HTMLElement {
2354
2354
  return InspectorView.maybeGetInspectorViewInstance()?.element || document.body;
2355
2355
  }
2356
+
2357
+ /**
2358
+ * @deprecated Prefer {@link bindToSetting} as this function leaks the checkbox via the setting listener.
2359
+ */
2360
+ export const bindCheckbox = function(
2361
+ input: CheckboxLabel, setting: Common.Settings.Setting<boolean>, metric?: UserMetricOptions): void {
2362
+ const setValue = bindCheckboxImpl(input, setting.set.bind(setting), metric);
2363
+ setting.addChangeListener(event => setValue(event.data));
2364
+ setValue(setting.get());
2365
+ };
2366
+
2367
+ export const bindCheckboxImpl = function(
2368
+ input: CheckboxLabel, apply: (value: boolean) => void, metric?: UserMetricOptions): (value: boolean) => void {
2369
+ input.addEventListener('change', onInputChanged, false);
2370
+
2371
+ function onInputChanged(): void {
2372
+ apply(input.checked);
2373
+
2374
+ if (input.checked && metric?.enable) {
2375
+ Host.userMetrics.actionTaken(metric.enable);
2376
+ }
2377
+
2378
+ if (!input.checked && metric?.disable) {
2379
+ Host.userMetrics.actionTaken(metric.disable);
2380
+ }
2381
+
2382
+ if (metric?.toggle) {
2383
+ Host.userMetrics.actionTaken(metric.toggle);
2384
+ }
2385
+ }
2386
+
2387
+ return function setValue(value: boolean): void {
2388
+ if (value !== input.checked) {
2389
+ input.checked = value;
2390
+ }
2391
+ };
2392
+ };
2393
+
2394
+ export const bindToSetting =
2395
+ (settingOrName: string|Common.Settings.Setting<boolean|string>|Common.Settings.RegExpSetting,
2396
+ stringValidator?: (newSettingValue: string) => boolean): ReturnType<typeof Directives.ref> => {
2397
+ const setting = typeof settingOrName === 'string' ?
2398
+ Common.Settings.Settings.instance().moduleSetting(settingOrName) :
2399
+ settingOrName;
2400
+
2401
+ // We can't use `setValue` as the change listener directly, otherwise we won't
2402
+ // be able to remove it again.
2403
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2404
+ let setValue: (value: any) => void;
2405
+ function settingChanged(): void {
2406
+ setValue(setting.get());
2407
+ }
2408
+
2409
+ if (setting.type() === Common.Settings.SettingType.BOOLEAN || typeof setting.defaultValue === 'boolean') {
2410
+ return Directives.ref(e => {
2411
+ if (e === undefined) {
2412
+ setting.removeChangeListener(settingChanged);
2413
+ return;
2414
+ }
2415
+
2416
+ setting.addChangeListener(settingChanged);
2417
+ setValue =
2418
+ bindCheckboxImpl(e as CheckboxLabel, (setting as Common.Settings.Setting<boolean>).set.bind(setting));
2419
+ setValue(setting.get());
2420
+ });
2421
+ }
2422
+
2423
+ if (setting.type() === Common.Settings.SettingType.REGEX || setting instanceof Common.Settings.RegExpSetting) {
2424
+ return Directives.ref(e => {
2425
+ if (e === undefined) {
2426
+ setting.removeChangeListener(settingChanged);
2427
+ return;
2428
+ }
2429
+
2430
+ setting.addChangeListener(settingChanged);
2431
+ setValue = bindInput(e as HTMLInputElement, setting.set.bind(setting), (value: string) => {
2432
+ try {
2433
+ new RegExp(value);
2434
+ return true;
2435
+ } catch {
2436
+ return false;
2437
+ }
2438
+ }, /* numeric */ false);
2439
+ setValue(setting.get());
2440
+ });
2441
+ }
2442
+
2443
+ if (typeof setting.defaultValue === 'string') {
2444
+ return Directives.ref(e => {
2445
+ if (e === undefined) {
2446
+ setting.removeChangeListener(settingChanged);
2447
+ return;
2448
+ }
2449
+
2450
+ setting.addChangeListener(settingChanged);
2451
+ setValue = bindInput(
2452
+ e as HTMLInputElement, setting.set.bind(setting), stringValidator ?? (() => true), /* numeric */ false);
2453
+ setValue(setting.get());
2454
+ });
2455
+ }
2456
+
2457
+ throw new Error(`Cannot infer type for setting '${setting.name}'`);
2458
+ };
2459
+
2460
+ /**
2461
+ * Track toggle action as a whole or
2462
+ * track on and off action separately.
2463
+ */
2464
+ export interface UserMetricOptions {
2465
+ toggle?: Host.UserMetrics.Action;
2466
+ enable?: Host.UserMetrics.Action;
2467
+ disable?: Host.UserMetrics.Action;
2468
+ }
@@ -68,6 +68,64 @@ export function widgetConfig<F extends WidgetFactory<Widget>, ParamKeys extends
68
68
  return new WidgetConfig(widgetClass, widgetParams);
69
69
  }
70
70
 
71
+ let currentUpdateQueue: Map<Widget, PromiseWithResolvers<void>>|null = null;
72
+ const currentlyProcessed = new Set<Widget>();
73
+ let nextUpdateQueue = new Map<Widget, PromiseWithResolvers<void>>();
74
+ let pendingAnimationFrame: number|null = null;
75
+
76
+ function enqueueIntoNextUpdateQueue(widget: Widget): Promise<void> {
77
+ const scheduledUpdate = nextUpdateQueue.get(widget) ?? Promise.withResolvers<void>();
78
+ nextUpdateQueue.delete(widget);
79
+ nextUpdateQueue.set(widget, scheduledUpdate);
80
+ if (pendingAnimationFrame === null) {
81
+ pendingAnimationFrame = requestAnimationFrame(runNextUpdate);
82
+ }
83
+ return scheduledUpdate.promise;
84
+ }
85
+
86
+ function enqueueWidgetUpdate(widget: Widget): Promise<void> {
87
+ if (currentUpdateQueue) {
88
+ if (currentlyProcessed.has(widget)) {
89
+ return enqueueIntoNextUpdateQueue(widget);
90
+ }
91
+ const scheduledUpdate = currentUpdateQueue.get(widget) ?? Promise.withResolvers<void>();
92
+ currentUpdateQueue.delete(widget);
93
+ currentUpdateQueue.set(widget, scheduledUpdate);
94
+ return scheduledUpdate.promise;
95
+ }
96
+ return enqueueIntoNextUpdateQueue(widget);
97
+ }
98
+
99
+ function cancelUpdate(widget: Widget): void {
100
+ if (currentUpdateQueue) {
101
+ const scheduledUpdate = currentUpdateQueue.get(widget);
102
+ if (scheduledUpdate) {
103
+ scheduledUpdate.resolve();
104
+ currentUpdateQueue.delete(widget);
105
+ }
106
+ }
107
+ const scheduledUpdate = nextUpdateQueue.get(widget);
108
+ if (scheduledUpdate) {
109
+ scheduledUpdate.resolve();
110
+ nextUpdateQueue.delete(widget);
111
+ }
112
+ }
113
+
114
+ function runNextUpdate(): void {
115
+ pendingAnimationFrame = null;
116
+ currentUpdateQueue = nextUpdateQueue;
117
+ nextUpdateQueue = new Map();
118
+ for (const [widget, {resolve}] of currentUpdateQueue) {
119
+ currentlyProcessed.add(widget);
120
+ void (async () => {
121
+ await widget.performUpdate();
122
+ resolve();
123
+ })();
124
+ }
125
+ currentUpdateQueue = null;
126
+ currentlyProcessed.clear();
127
+ }
128
+
71
129
  export class WidgetElement<WidgetT extends Widget> extends HTMLElement {
72
130
  #widgetClass?: WidgetFactory<WidgetT>;
73
131
  #widgetParams?: Partial<WidgetT>;
@@ -218,8 +276,7 @@ function decrementWidgetCounter(parentElement: Element, childElement: Element):
218
276
  // The resolved `updateComplete` promise, which is used as a marker for the
219
277
  // Widget's `#updateComplete` private property to indicate that there's no
220
278
  // pending update.
221
- const UPDATE_COMPLETE = Promise.resolve(true);
222
- const UPDATE_COMPLETE_RESOLVE = (_result: boolean): void => {};
279
+ const UPDATE_COMPLETE = Promise.resolve();
223
280
 
224
281
  /**
225
282
  * Additional options passed to the `Widget` constructor to configure the
@@ -277,8 +334,6 @@ export class Widget {
277
334
  #invalidationsRequested?: boolean;
278
335
  #externallyManaged?: boolean;
279
336
  #updateComplete = UPDATE_COMPLETE;
280
- #updateCompleteResolve = UPDATE_COMPLETE_RESOLVE;
281
- #updateRequestID = 0;
282
337
 
283
338
  /**
284
339
  * Constructs a new `Widget` with the given `options`.
@@ -617,14 +672,7 @@ export class Widget {
617
672
  return;
618
673
  }
619
674
 
620
- // Cancel any pending update.
621
- if (this.#updateRequestID !== 0) {
622
- cancelAnimationFrame(this.#updateRequestID);
623
- this.#updateCompleteResolve(true);
624
- this.#updateCompleteResolve = UPDATE_COMPLETE_RESOLVE;
625
- this.#updateComplete = UPDATE_COMPLETE;
626
- this.#updateRequestID = 0;
627
- }
675
+ cancelUpdate(this);
628
676
 
629
677
  // hideOnDetach means that we should never remove element from dom - content
630
678
  // has iframes and detaching it will hurt.
@@ -876,21 +924,6 @@ export class Widget {
876
924
  performUpdate(): Promise<void>|void {
877
925
  }
878
926
 
879
- async #performUpdateCallback(): Promise<boolean> {
880
- // Mark this update cycle as complete by assigning
881
- // the marker sentinel.
882
- this.#updateComplete = UPDATE_COMPLETE;
883
- this.#updateCompleteResolve = UPDATE_COMPLETE_RESOLVE;
884
- this.#updateRequestID = 0;
885
-
886
- // Run the actual update logic.
887
- await this.performUpdate();
888
-
889
- // Resolve the `updateComplete` with `true` if no
890
- // new update was triggered during this cycle.
891
- return this.#updateComplete === UPDATE_COMPLETE;
892
- }
893
-
894
927
  /**
895
928
  * Schedules an asynchronous update for this widget.
896
929
  *
@@ -898,12 +931,7 @@ export class Widget {
898
931
  * frame.
899
932
  */
900
933
  requestUpdate(): void {
901
- if (this.#updateComplete === UPDATE_COMPLETE) {
902
- this.#updateComplete = new Promise((resolve, reject) => {
903
- this.#updateCompleteResolve = resolve;
904
- this.#updateRequestID = requestAnimationFrame(() => this.#performUpdateCallback().then(resolve, reject));
905
- });
906
- }
934
+ this.#updateComplete = enqueueWidgetUpdate(this);
907
935
  }
908
936
 
909
937
  /**
@@ -931,7 +959,7 @@ export class Widget {
931
959
  * updating, the value is `true` if there are no more pending updates,
932
960
  * and `false` if the update cycle triggered another update.
933
961
  */
934
- get updateComplete(): Promise<boolean> {
962
+ get updateComplete(): Promise<void> {
935
963
  return this.#updateComplete;
936
964
  }
937
965
  }
@@ -4,7 +4,6 @@
4
4
 
5
5
  import * as Host from '../../core/host/host.js';
6
6
  import * as Platform from '../../core/platform/platform.js';
7
- import {html} from '../lit/lit.js';
8
7
  import * as VisualLogging from '../visual_logging/visual_logging.js';
9
8
 
10
9
  import * as ARIAUtils from './ARIAUtils.js';
@@ -155,5 +154,3 @@ export class ContextMenuProvider implements Provider<Node> {
155
154
  }
156
155
 
157
156
  customElements.define('x-link', XLink);
158
-
159
- export const sample = html`<p>Hello, <x-link>world!</x-link></p>`;
@@ -372,6 +372,15 @@ class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate {
372
372
  this.#updateColumns();
373
373
  }
374
374
  this.#updateCreationNode();
375
+
376
+ const hadAddedNodes = mutationList.some(m => m.addedNodes.length > 0);
377
+ // If we got an update, and the data grid is sorted, we need to update the
378
+ // columns to maintain the sort order as the data within has changed.
379
+ // However, if we have nodes added, that will trigger a sort anyway so we
380
+ // don't need to re-sort again.
381
+ if (this.#dataGrid.sortColumnId() !== null && !hadAddedNodes) {
382
+ this.#dataGrid.dispatchEventToListeners(DataGridEvents.SORTING_CHANGED);
383
+ }
375
384
  }
376
385
 
377
386
  #editCallback(