chrome-devtools-frontend 1.0.1518653 → 1.0.1520139

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 (143) hide show
  1. package/config/owner/COMMON_OWNERS +1 -2
  2. package/config/typescript/tsconfig.eslint.json +12 -1
  3. package/docs/ui_engineering.md +1011 -0
  4. package/eslint.config.mjs +1 -0
  5. package/front_end/core/host/GdpClient.ts +12 -3
  6. package/front_end/core/sdk/EnhancedTracesParser.ts +5 -5
  7. package/front_end/core/sdk/NetworkManager.ts +1 -0
  8. package/front_end/core/sdk/NetworkRequest.ts +10 -0
  9. package/front_end/core/sdk/RehydratingConnection.snapshot.txt +211 -0
  10. package/front_end/core/sdk/TargetManager.ts +4 -0
  11. package/front_end/entrypoints/main/MainImpl.ts +6 -1
  12. package/front_end/entrypoints/main/main-meta.ts +3 -3
  13. package/front_end/generated/SupportedCSSProperties.js +19 -4
  14. package/front_end/models/ai_assistance/agents/AiAgent.ts +57 -10
  15. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +64 -87
  16. package/front_end/models/ai_assistance/agents/PerformanceAnnotationsAgent.ts +4 -4
  17. package/front_end/models/ai_assistance/agents/StylingAgent.ts +0 -31
  18. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +127 -29
  19. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +106 -55
  20. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +317 -640
  21. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +23 -19
  22. package/front_end/models/ai_assistance/performance/AICallTree.snapshot.txt +75 -0
  23. package/front_end/models/ai_assistance/performance/AICallTree.ts +14 -6
  24. package/front_end/models/ai_assistance/performance/AIContext.ts +63 -8
  25. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +5 -0
  26. package/front_end/models/badges/AiExplorerBadge.ts +19 -3
  27. package/front_end/models/badges/Badge.ts +8 -1
  28. package/front_end/models/badges/CodeWhispererBadge.ts +1 -0
  29. package/front_end/models/badges/DOMDetectiveBadge.ts +1 -0
  30. package/front_end/models/badges/SpeedsterBadge.ts +1 -0
  31. package/front_end/models/badges/StarterBadge.ts +6 -0
  32. package/front_end/models/badges/badges.ts +1 -0
  33. package/front_end/models/javascript_metadata/NativeFunctions.js +4 -0
  34. package/front_end/models/trace/EventsSerializer.ts +4 -3
  35. package/front_end/models/trace/handlers/UserInteractionsHandler.ts +101 -73
  36. package/front_end/models/trace/helpers/Timing.ts +1 -1
  37. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +18 -8
  38. package/front_end/panels/ai_assistance/PatchWidget.ts +17 -55
  39. package/front_end/panels/ai_assistance/components/ChatView.ts +44 -68
  40. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +63 -15
  41. package/front_end/panels/ai_assistance/components/chatView.css +12 -0
  42. package/front_end/panels/animation/AnimationTimeline.ts +1 -1
  43. package/front_end/panels/animation/animationTimeline.css +4 -0
  44. package/front_end/panels/application/components/BounceTrackingMitigationsView.ts +2 -2
  45. package/front_end/panels/application/preloading/components/PreloadingString.ts +2 -5
  46. package/front_end/panels/common/AiCodeCompletionTeaser.ts +5 -0
  47. package/front_end/panels/common/BadgeNotification.ts +3 -3
  48. package/front_end/panels/common/GdpSignUpDialog.ts +3 -4
  49. package/front_end/panels/common/aiCodeCompletionTeaser.css +6 -1
  50. package/front_end/panels/console/ConsolePrompt.ts +6 -0
  51. package/front_end/panels/console/ConsoleView.ts +4 -2
  52. package/front_end/panels/coverage/CoverageListView.ts +133 -158
  53. package/front_end/panels/coverage/CoverageView.ts +39 -16
  54. package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +5 -5
  55. package/front_end/panels/mobile_throttling/NetworkThrottlingSelector.ts +2 -0
  56. package/front_end/panels/network/NetworkDataGridNode.ts +22 -0
  57. package/front_end/panels/network/NetworkLogViewColumns.ts +9 -0
  58. package/front_end/panels/recorder/components/CreateRecordingView.ts +2 -0
  59. package/front_end/panels/recorder/components/RecordingView.ts +2 -2
  60. package/front_end/panels/search/SearchResultsPane.ts +186 -134
  61. package/front_end/panels/search/SearchView.ts +42 -36
  62. package/front_end/panels/search/searchResultsPane.css +9 -0
  63. package/front_end/panels/search/searchView.css +0 -2
  64. package/front_end/panels/security/CookieControlsView.ts +2 -1
  65. package/front_end/panels/settings/AISettingsTab.ts +6 -3
  66. package/front_end/panels/settings/components/SyncSection.ts +26 -12
  67. package/front_end/panels/settings/emulation/components/UserAgentClientHintsForm.ts +1 -1
  68. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +4 -4
  69. package/front_end/panels/sources/DebuggerPlugin.ts +4 -0
  70. package/front_end/panels/sources/SourcesPanel.ts +1 -1
  71. package/front_end/panels/sources/sourcesView.css +6 -1
  72. package/front_end/panels/timeline/ThirdPartyTreeView.ts +1 -1
  73. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +0 -8
  74. package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -5
  75. package/front_end/panels/timeline/TimelinePanel.ts +2 -0
  76. package/front_end/panels/timeline/TimelineTreeView.ts +1 -1
  77. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +1 -1
  78. package/front_end/panels/timeline/components/NetworkRequestDetails.ts +1 -1
  79. package/front_end/panels/timeline/components/RelatedInsightChips.ts +1 -1
  80. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +1 -1
  81. package/front_end/third_party/chromium/README.chromium +1 -1
  82. package/front_end/third_party/puppeteer/README.chromium +2 -2
  83. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
  84. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.d.ts +1 -1
  85. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.js +1 -1
  86. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
  87. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +1 -1
  88. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  89. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js +1 -1
  90. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/BrowserLauncher.js.map +1 -1
  91. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
  92. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js +1 -0
  93. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js.map +1 -1
  94. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.d.ts.map +1 -1
  95. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js +15 -16
  96. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/PipeTransport.js.map +1 -1
  97. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
  98. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
  99. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  100. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.d.ts.map +1 -1
  101. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js +16 -25
  102. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js.map +1 -1
  103. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  104. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +19 -28
  105. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.d.ts +1 -1
  106. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.js +1 -1
  107. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
  108. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
  109. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js +1 -1
  110. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/BrowserLauncher.js.map +1 -1
  111. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
  112. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js +1 -0
  113. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js.map +1 -1
  114. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.d.ts.map +1 -1
  115. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js +15 -16
  116. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/PipeTransport.js.map +1 -1
  117. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
  118. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
  119. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  120. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.d.ts.map +1 -1
  121. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js +16 -25
  122. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js.map +1 -1
  123. package/front_end/third_party/puppeteer/package/package.json +12 -4
  124. package/front_end/third_party/puppeteer/package/src/generated/injected.ts +1 -1
  125. package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
  126. package/front_end/third_party/puppeteer/package/src/node/BrowserLauncher.ts +1 -1
  127. package/front_end/third_party/puppeteer/package/src/node/ChromeLauncher.ts +1 -0
  128. package/front_end/third_party/puppeteer/package/src/node/PipeTransport.ts +15 -17
  129. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  130. package/front_end/third_party/puppeteer/package/src/util/Function.ts +22 -30
  131. package/front_end/tsconfig.json +12 -1
  132. package/front_end/ui/components/dialogs/Dialog.ts +1 -1
  133. package/front_end/ui/components/markdown_view/MarkdownImage.ts +4 -5
  134. package/front_end/ui/components/switch/SwitchImpl.ts +12 -1
  135. package/front_end/ui/components/text_editor/config.ts +16 -2
  136. package/front_end/ui/legacy/InspectorView.ts +86 -13
  137. package/front_end/ui/legacy/TabbedPane.ts +2 -1
  138. package/front_end/ui/legacy/Treeoutline.ts +3 -1
  139. package/front_end/ui/legacy/components/source_frame/XMLView.ts +12 -11
  140. package/front_end/ui/lit/i18n-template.ts +5 -2
  141. package/front_end/ui/visual_logging/KnownContextValues.ts +15 -5
  142. package/front_end/ui/visual_logging/LoggingEvents.ts +1 -1
  143. package/package.json +1 -1
@@ -17,7 +17,7 @@ export class PipeTransport implements ConnectionTransport {
17
17
  #subscriptions = new DisposableStack();
18
18
 
19
19
  #isClosed = false;
20
- #pendingMessage = '';
20
+ #pendingMessage: Buffer[] = [];
21
21
 
22
22
  onclose?: () => void;
23
23
  onmessage?: (value: string) => void;
@@ -34,7 +34,7 @@ export class PipeTransport implements ConnectionTransport {
34
34
  pipeRead as unknown as EventEmitter<Record<string, any>>,
35
35
  ),
36
36
  );
37
- pipeReadEmitter.on('data', (buffer: Buffer) => {
37
+ pipeReadEmitter.on('data', buffer => {
38
38
  return this.#dispatch(buffer);
39
39
  });
40
40
  pipeReadEmitter.on('close', () => {
@@ -60,34 +60,32 @@ export class PipeTransport implements ConnectionTransport {
60
60
  this.#pipeWrite.write('\0');
61
61
  }
62
62
 
63
- #dispatch(buffer: Buffer): void {
63
+ #dispatch(buffer: Buffer<ArrayBuffer>): void {
64
64
  assert(!this.#isClosed, '`PipeTransport` is closed.');
65
65
 
66
- let end = buffer.indexOf('\0');
67
- if (end === -1) {
68
- this.#pendingMessage += buffer.toString();
66
+ this.#pendingMessage.push(buffer);
67
+ if (buffer.indexOf('\0') === -1) {
69
68
  return;
70
69
  }
71
- const message = this.#pendingMessage + buffer.toString(undefined, 0, end);
72
- setImmediate(() => {
73
- if (this.onmessage) {
74
- this.onmessage.call(null, message);
75
- }
76
- });
70
+ const concatBuffer = Buffer.concat(this.#pendingMessage);
77
71
 
78
- let start = end + 1;
79
- end = buffer.indexOf('\0', start);
72
+ let start = 0;
73
+ let end = concatBuffer.indexOf('\0');
80
74
  while (end !== -1) {
81
- const message = buffer.toString(undefined, start, end);
75
+ const message = concatBuffer.toString(undefined, start, end);
82
76
  setImmediate(() => {
83
77
  if (this.onmessage) {
84
78
  this.onmessage.call(null, message);
85
79
  }
86
80
  });
87
81
  start = end + 1;
88
- end = buffer.indexOf('\0', start);
82
+ end = concatBuffer.indexOf('\0', start);
83
+ }
84
+ if (start >= concatBuffer.length) {
85
+ this.#pendingMessage = [];
86
+ } else {
87
+ this.#pendingMessage = [concatBuffer.subarray(start)];
89
88
  }
90
- this.#pendingMessage = buffer.toString(undefined, start);
91
89
  }
92
90
 
93
91
  close(): void {
@@ -8,7 +8,7 @@
8
8
  * @internal
9
9
  */
10
10
  export const PUPPETEER_REVISIONS = Object.freeze({
11
- chrome: '140.0.7339.82',
12
- 'chrome-headless-shell': '140.0.7339.82',
13
- firefox: 'stable_143.0',
11
+ chrome: '140.0.7339.207',
12
+ 'chrome-headless-shell': '140.0.7339.207',
13
+ firefox: 'stable_143.0.1',
14
14
  });
@@ -29,37 +29,29 @@ export const createFunction = (
29
29
  */
30
30
  export function stringifyFunction(fn: (...args: never) => unknown): string {
31
31
  let value = fn.toString();
32
- try {
33
- new Function(`(${value})`);
34
- } catch (err) {
35
- if (
36
- (err as Error).message.includes(
37
- `Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive`,
38
- ) ||
39
- (err as Error).message.includes(
40
- 'Evaluating a string as JavaScript violates the following Content Security Policy',
41
- )
42
- ) {
43
- // The content security policy does not allow Function eval. Let's
44
- // assume the value might be valid as is.
45
- return value;
46
- }
47
- // This means we might have a function shorthand (e.g. `test(){}`). Let's
48
- // try prefixing.
49
- let prefix = 'function ';
50
- if (value.startsWith('async ')) {
51
- prefix = `async ${prefix}`;
52
- value = value.substring('async '.length);
53
- }
54
- value = `${prefix}${value}`;
55
- try {
56
- new Function(`(${value})`);
57
- } catch {
58
- // We tried hard to serialize, but there's a weird beast here.
59
- throw new Error('Passed function cannot be serialized!');
60
- }
32
+ if (
33
+ value.match(/^(async )*function(\(|\s)/) ||
34
+ value.match(/^(async )*function\s*\*\s*/)
35
+ ) {
36
+ return value;
61
37
  }
62
- return value;
38
+ const isArrow =
39
+ value.startsWith('(') ||
40
+ value.match(/^async\s*\(/) ||
41
+ value.match(
42
+ /^(async)*\s*(?:[$_\p{ID_Start}])(?:[$\u200C\u200D\p{ID_Continue}])*\s*=>/u,
43
+ );
44
+ if (isArrow) {
45
+ return value;
46
+ }
47
+ // This means we might have a function shorthand (e.g. `test(){}`). Let's
48
+ // try prefixing.
49
+ let prefix = 'function ';
50
+ if (value.startsWith('async ')) {
51
+ prefix = `async ${prefix}`;
52
+ value = value.substring('async '.length);
53
+ }
54
+ return `${prefix}${value}`;
63
55
  }
64
56
 
65
57
  /**
@@ -3,7 +3,18 @@
3
3
  "compilerOptions": {
4
4
  "allowUmdGlobalAccess": true,
5
5
  "outDir": "ignored",
6
- "lib": ["esnext", "dom", "dom.iterable", "webworker", "webworker.iterable"],
6
+ "target": "ES2023",
7
+ "lib": [
8
+ "ES2023",
9
+ "ES2024.Promise",
10
+ "ESNext.Iterator",
11
+ "ESNext.Collection",
12
+ "ESNext.Array",
13
+ "dom",
14
+ "dom.iterable",
15
+ "webworker",
16
+ "webworker.iterable"
17
+ ],
7
18
  "plugins": [
8
19
  {
9
20
  "name": "ts-lit-plugin",
@@ -704,7 +704,7 @@ export class Dialog extends HTMLElement {
704
704
  return;
705
705
  }
706
706
 
707
- let dialogContent = html``;
707
+ let dialogContent: Lit.LitTemplate = Lit.nothing;
708
708
 
709
709
  // If state is expanded content should be shown, do not render it otherwise.
710
710
  if (this.#props.state === DialogState.EXPANDED) {
@@ -24,7 +24,6 @@ export interface MarkdownImageData {
24
24
  * This makes sure that all icons/images are accounted for in markdown.
25
25
  */
26
26
  export class MarkdownImage extends HTMLElement {
27
-
28
27
  readonly #shadow = this.attachShadow({mode: 'open'});
29
28
  #imageData?: ImageData;
30
29
  #imageTitle?: string;
@@ -37,9 +36,9 @@ export class MarkdownImage extends HTMLElement {
37
36
  this.#render();
38
37
  }
39
38
 
40
- #getIconComponent(): Lit.TemplateResult {
39
+ #getIconComponent(): Lit.LitTemplate {
41
40
  if (!this.#imageData) {
42
- return html``;
41
+ return Lit.nothing;
43
42
  }
44
43
  const {src, color, width = '100%', height = '100%'} = this.#imageData;
45
44
  return html`
@@ -47,9 +46,9 @@ export class MarkdownImage extends HTMLElement {
47
46
  `;
48
47
  }
49
48
 
50
- #getImageComponent(): Lit.TemplateResult {
49
+ #getImageComponent(): Lit.LitTemplate {
51
50
  if (!this.#imageData) {
52
- return html``;
51
+ return Lit.nothing;
53
52
  }
54
53
  const {src, width = '100%', height = '100%'} = this.#imageData;
55
54
  return html`
@@ -21,6 +21,7 @@ export class Switch extends HTMLElement {
21
21
  #checked = false;
22
22
  #disabled = false;
23
23
  #jslogContext = '';
24
+ #label = '';
24
25
 
25
26
  connectedCallback(): void {
26
27
  this.#render();
@@ -53,6 +54,15 @@ export class Switch extends HTMLElement {
53
54
  this.#render();
54
55
  }
55
56
 
57
+ get label(): string {
58
+ return this.#label;
59
+ }
60
+
61
+ set label(label: string) {
62
+ this.#label = label;
63
+ this.#render();
64
+ }
65
+
56
66
  #handleChange = (ev: Event): void => {
57
67
  this.#checked = (ev.target as HTMLInputElement).checked;
58
68
  this.dispatchEvent(new SwitchChangeEvent(this.#checked));
@@ -64,8 +74,9 @@ export class Switch extends HTMLElement {
64
74
  // clang-format off
65
75
  render(html`
66
76
  <style>${switchStyles}</style>
67
- <label role="button" jslog=${jslog || nothing}>
77
+ <label jslog=${jslog || nothing}>
68
78
  <input type="checkbox"
79
+ aria-label=${this.#label || nothing}
69
80
  @change=${this.#handleChange}
70
81
  ?disabled=${this.#disabled}
71
82
  .checked=${this.#checked}
@@ -488,6 +488,7 @@ interface ActiveSuggestion {
488
488
  rpcGlobalId?: Host.AidaClient.RpcGlobalId;
489
489
  startTime: number;
490
490
  onImpression: (rpcGlobalId: Host.AidaClient.RpcGlobalId, sampleId: number, latency: number) => void;
491
+ clearCachedRequest: () => void;
491
492
  }
492
493
 
493
494
  export const aiAutoCompleteSuggestionState = CM.StateField.define<ActiveSuggestion|null>({
@@ -498,6 +499,7 @@ export const aiAutoCompleteSuggestionState = CM.StateField.define<ActiveSuggesti
498
499
  if (effect.value) {
499
500
  return effect.value;
500
501
  }
502
+ value?.clearCachedRequest();
501
503
  return null;
502
504
  }
503
505
  }
@@ -509,13 +511,15 @@ export const aiAutoCompleteSuggestionState = CM.StateField.define<ActiveSuggesti
509
511
  // A suggestion from an effect can be stale if the document was changed
510
512
  // between when the request was sent and the response was received.
511
513
  // We check if the position is still valid before trying to map it.
512
- if (value.from > tr.startState.doc.length) {
514
+ if (value.from > tr.state.doc.length) {
515
+ value.clearCachedRequest();
513
516
  return null;
514
517
  }
515
518
 
516
519
  // If deletion occurs, set to null. Otherwise, the mapping might fail if
517
520
  // the position is inside the deleted range.
518
521
  if (tr.docChanged && tr.state.doc.length < tr.startState.doc.length) {
522
+ value.clearCachedRequest();
519
523
  return null;
520
524
  }
521
525
 
@@ -523,7 +527,8 @@ export const aiAutoCompleteSuggestionState = CM.StateField.define<ActiveSuggesti
523
527
  const {head} = tr.state.selection.main;
524
528
 
525
529
  // If a change happened before the position from which suggestion was generated, set to null.
526
- if (head < from) {
530
+ if (tr.docChanged && head < from) {
531
+ value.clearCachedRequest();
527
532
  return null;
528
533
  }
529
534
 
@@ -563,6 +568,8 @@ export function acceptAiAutoCompleteSuggestion(view: CM.EditorView):
563
568
  effects: setAiAutoCompleteSuggestion.of(null),
564
569
  userEvent: 'input.complete',
565
570
  });
571
+
572
+ suggestion.clearCachedRequest();
566
573
  return {accepted: true, suggestion};
567
574
  }
568
575
 
@@ -603,6 +610,13 @@ export const aiAutoCompleteSuggestion: CM.Extension = [
603
610
  }
604
611
 
605
612
  const {head} = update.state.selection.main;
613
+ // Hide AI suggestion if the user moves the cursor to a location
614
+ // before the position from which suggestion was generated.
615
+ if (head < activeSuggestion.from) {
616
+ this.decorations = CM.Decoration.none;
617
+ return;
618
+ }
619
+
606
620
  const selectedCompletion = CM.selectedCompletion(update.state);
607
621
  const additionallyTypedText = update.state.doc.sliceString(activeSuggestion.from, head);
608
622
  // The user might have typed text after the suggestion is triggered.
@@ -18,7 +18,7 @@ import * as ARIAUtils from './ARIAUtils.js';
18
18
  import type {Context} from './Context.js';
19
19
  import type {ContextMenu} from './ContextMenu.js';
20
20
  import {Dialog} from './Dialog.js';
21
- import {DockController, DockState} from './DockController.js';
21
+ import {DockController, DockState, Events as DockControllerEvents} from './DockController.js';
22
22
  import {GlassPane} from './GlassPane.js';
23
23
  import {Infobar, Type as InfobarType} from './Infobar.js';
24
24
  import {KeyboardShortcut} from './KeyboardShortcut.js';
@@ -127,8 +127,20 @@ export enum DrawerOrientation {
127
127
  UNSET = 'unset',
128
128
  }
129
129
 
130
+ export enum DockMode {
131
+ BOTTOM = 'bottom',
132
+ SIDE = 'side', // For LEFT and RIGHT
133
+ UNDOCKED = 'undocked',
134
+ }
135
+
136
+ export interface DrawerOrientationByDockMode {
137
+ [DockMode.BOTTOM]: DrawerOrientation;
138
+ [DockMode.SIDE]: DrawerOrientation;
139
+ [DockMode.UNDOCKED]: DrawerOrientation;
140
+ }
141
+
130
142
  export class InspectorView extends VBox implements ViewLocationResolver {
131
- private readonly drawerOrientationSetting: Common.Settings.Setting<DrawerOrientation>;
143
+ private readonly drawerOrientationByDockSetting: Common.Settings.Setting<DrawerOrientationByDockMode>;
132
144
  private readonly drawerSplitWidget: SplitWidget;
133
145
  private readonly tabDelegate: InspectorViewTabDelegate;
134
146
  private readonly drawerTabbedLocation: TabbedViewLocation;
@@ -151,9 +163,14 @@ export class InspectorView extends VBox implements ViewLocationResolver {
151
163
  this.setMinimumSize(MIN_INSPECTOR_WIDTH_HORIZONTAL_DRAWER, MIN_INSPECTOR_HEIGHT);
152
164
 
153
165
  // DevTools sidebar is a vertical split of main tab bar panels and a drawer.
154
- this.drawerOrientationSetting =
155
- Common.Settings.Settings.instance().createSetting('inspector.drawer-orientation', DrawerOrientation.UNSET);
156
- const isVertical = this.drawerOrientationSetting.get() === DrawerOrientation.VERTICAL;
166
+ this.drawerOrientationByDockSetting =
167
+ Common.Settings.Settings.instance().createSetting('inspector.drawer-orientation-by-dock-mode', {
168
+ [DockMode.BOTTOM]: DrawerOrientation.UNSET,
169
+ [DockMode.SIDE]: DrawerOrientation.UNSET,
170
+ [DockMode.UNDOCKED]: DrawerOrientation.UNSET,
171
+ });
172
+ const initialOrientation = this.#getOrientationForDockMode();
173
+ const isVertical = initialOrientation === DrawerOrientation.VERTICAL;
157
174
  this.drawerSplitWidget = new SplitWidget(isVertical, true, 'inspector.drawer-split-view-state', 200, 200);
158
175
  this.drawerSplitWidget.hideSidebar();
159
176
  this.drawerSplitWidget.enableShowModeSaving();
@@ -285,6 +302,50 @@ export class InspectorView extends VBox implements ViewLocationResolver {
285
302
  inspectorViewInstance = null;
286
303
  }
287
304
 
305
+ onDockSideChangedHandledForTest(): void {
306
+ }
307
+
308
+ #onDockSideChanged(): void {
309
+ if (!this.drawerVisible()) {
310
+ this.onDockSideChangedHandledForTest();
311
+ return;
312
+ }
313
+ const newOrientation = this.#getOrientationForDockMode();
314
+ this.#applyOrientation(newOrientation);
315
+ this.onDockSideChangedHandledForTest();
316
+ }
317
+
318
+ #getDockMode(): DockMode {
319
+ const dockSide = DockController.instance().dockSide();
320
+ if (dockSide === DockState.BOTTOM) {
321
+ return DockMode.BOTTOM;
322
+ }
323
+ if (dockSide === DockState.UNDOCKED) {
324
+ return DockMode.UNDOCKED;
325
+ }
326
+
327
+ return DockMode.SIDE;
328
+ }
329
+
330
+ #getOrientationForDockMode(): Omit<DrawerOrientation, DrawerOrientation.UNSET> {
331
+ const dockMode = this.#getDockMode();
332
+ const orientationSetting = this.drawerOrientationByDockSetting.get();
333
+
334
+ let orientation = orientationSetting[dockMode];
335
+ if (orientation === DrawerOrientation.UNSET) {
336
+ // Apply defaults: horizontal for side-dock, vertical for bottom-dock.
337
+ orientation = dockMode === DockMode.BOTTOM ? DrawerOrientation.VERTICAL : DrawerOrientation.HORIZONTAL;
338
+ }
339
+ return orientation;
340
+ }
341
+
342
+ #applyOrientation(orientation: Omit<DrawerOrientation, DrawerOrientation.UNSET>): void {
343
+ const isVertical = orientation === DrawerOrientation.VERTICAL;
344
+ this.#toggleOrientationButton.setGlyph(isVertical ? 'dock-bottom' : 'dock-right');
345
+ this.drawerSplitWidget.setVertical(isVertical);
346
+ this.setDrawerRelatedMinimumSizes();
347
+ }
348
+
288
349
  #observedResize(): void {
289
350
  const rect = this.element.getBoundingClientRect();
290
351
  this.element.style.setProperty('--devtools-window-left', `${rect.left}px`);
@@ -299,11 +360,15 @@ export class InspectorView extends VBox implements ViewLocationResolver {
299
360
  this.#resizeObserver.observe(this.element);
300
361
  this.#observedResize();
301
362
  this.element.ownerDocument.addEventListener('keydown', this.keyDownBound, false);
363
+ DockController.instance().addEventListener(DockControllerEvents.DOCK_SIDE_CHANGED, this.#onDockSideChanged, this);
364
+ this.#onDockSideChanged();
302
365
  }
303
366
 
304
367
  override willHide(): void {
305
368
  this.#resizeObserver.unobserve(this.element);
306
369
  this.element.ownerDocument.removeEventListener('keydown', this.keyDownBound, false);
370
+ DockController.instance().removeEventListener(
371
+ DockControllerEvents.DOCK_SIDE_CHANGED, this.#onDockSideChanged, this);
307
372
  }
308
373
 
309
374
  resolveLocation(locationName: string): ViewLocation|null {
@@ -421,21 +486,29 @@ export class InspectorView extends VBox implements ViewLocationResolver {
421
486
  if (!this.drawerTabbedPane.isShowing()) {
422
487
  return;
423
488
  }
424
- let drawerWillBeVertical: boolean;
489
+
490
+ const dockMode = this.#getDockMode();
491
+ const currentSettings = this.drawerOrientationByDockSetting.get();
492
+
493
+ let newOrientation: Omit<DrawerOrientation, DrawerOrientation.UNSET>;
425
494
  if (force) {
426
- drawerWillBeVertical = force === DrawerOrientation.VERTICAL;
495
+ newOrientation = force;
427
496
  } else {
428
- drawerWillBeVertical = !this.drawerSplitWidget.isVertical();
497
+ const currentOrientation = this.#getOrientationForDockMode();
498
+ newOrientation =
499
+ currentOrientation === DrawerOrientation.VERTICAL ? DrawerOrientation.HORIZONTAL : DrawerOrientation.VERTICAL;
429
500
  }
430
501
 
431
- this.drawerOrientationSetting.set(drawerWillBeVertical ? DrawerOrientation.VERTICAL : DrawerOrientation.HORIZONTAL);
432
- this.#toggleOrientationButton.setGlyph(drawerWillBeVertical ? 'dock-bottom' : 'dock-right');
433
- this.drawerSplitWidget.setVertical(drawerWillBeVertical);
434
- this.setDrawerRelatedMinimumSizes();
502
+ currentSettings[dockMode] = newOrientation as DrawerOrientation;
503
+ this.drawerOrientationByDockSetting.set(currentSettings);
504
+
505
+ this.#applyOrientation(newOrientation);
435
506
  }
436
507
 
437
508
  isUserExplicitlyUpdatedDrawerOrientation(): boolean {
438
- return this.drawerOrientationSetting.get() !== DrawerOrientation.UNSET;
509
+ const orientationSetting = this.drawerOrientationByDockSetting.get();
510
+ const dockMode = this.#getDockMode();
511
+ return orientationSetting[dockMode] !== DrawerOrientation.UNSET;
439
512
  }
440
513
 
441
514
  setDrawerRelatedMinimumSizes(): void {
@@ -326,6 +326,8 @@ export class TabbedPane extends Common.ObjectWrapper.eventMixin<EventTypes, type
326
326
  return false;
327
327
  }
328
328
 
329
+ this.lastSelectedOverflowTab = tab;
330
+
329
331
  const eventData: EventData = {
330
332
  prevTabId: this.currentTab ? this.currentTab.id : undefined,
331
333
  tabId: id,
@@ -676,7 +678,6 @@ export class TabbedPane extends Common.ObjectWrapper.eventMixin<EventTypes, type
676
678
  }
677
679
 
678
680
  private dropDownMenuItemSelected(tab: TabbedPaneTab): void {
679
- this.lastSelectedOverflowTab = tab;
680
681
  this.selectTab(tab.id, true, true);
681
682
  }
682
683
 
@@ -1455,7 +1455,7 @@ export class TreeSearch < NodeT extends TreeNode<NodeT>,
1455
1455
  return this.#getNodeMatchMap().get(node) ?? [];
1456
1456
  }
1457
1457
 
1458
- highlight(ranges: TextUtils.TextRange.SourceRange[], selectedRange: TextUtils.TextRange.SourceRange|undefined):
1458
+ static highlight(ranges: TextUtils.TextRange.SourceRange[], selectedRange: TextUtils.TextRange.SourceRange|undefined):
1459
1459
  ReturnType<typeof Lit.Directives.ref> {
1460
1460
  return Lit.Directives.ref(element => {
1461
1461
  if (element instanceof HTMLLIElement) {
@@ -1777,6 +1777,8 @@ export class TreeViewElement extends HTMLElementWithLightDOMTemplate {
1777
1777
  const nextElement = nextSibling ? TreeViewTreeElement.get(nextSibling) : null;
1778
1778
  const index = nextElement ? parent.treeElement.indexOfChild(nextElement) : parent.treeElement.children().length;
1779
1779
  const treeElement = new TreeViewTreeElement(this.#treeOutline, node);
1780
+ const expandable = Boolean(node.querySelector('ul[role="group"]'));
1781
+ treeElement.setExpandable(expandable);
1780
1782
  parent.treeElement.insertChild(treeElement, index);
1781
1783
  if (hasBooleanAttribute(node, 'selected')) {
1782
1784
  treeElement.revealAndSelect(true);
@@ -129,7 +129,7 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
129
129
  }
130
130
  }
131
131
  }
132
- return input.search.highlight(highlights, selected);
132
+ return UI.TreeOutline.TreeSearch.highlight(highlights, selected);
133
133
  }
134
134
 
135
135
  function layOutNode(node: XMLTreeViewNode, populateSubtrees = false): Lit.LitTemplate {
@@ -138,13 +138,15 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
138
138
 
139
139
  // clang-format off
140
140
  return html`
141
- <li
142
- ${highlight(node, /* closeTag=*/ false)}
143
- role="treeitem"
144
- ?selected=${input.jumpToNextSearchResult?.node === node}
145
- @expand=${onExpand}>
146
- ${htmlView(node)}${populateSubtrees || input.search ? subtree(node) : Lit.nothing}
147
- </li>`;
141
+ <li ${highlight(node, /* closeTag=*/ false)} role="treeitem"
142
+ ?selected=${input.jumpToNextSearchResult?.node === node}
143
+ @expand=${onExpand}>
144
+ ${htmlView(node)}
145
+ ${node.children().length ? html`
146
+ <ul role="group" ?hidden=${!node.expanded}>
147
+ ${populateSubtrees || input.search ? subtree(node) : Lit.nothing}
148
+ </ul>` : Lit.nothing}
149
+ </li>`;
148
150
  // clang-format on
149
151
  }
150
152
 
@@ -154,15 +156,14 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
154
156
  return Lit.nothing;
155
157
  }
156
158
  // clang-format off
157
- return html`<ul role="group" ?hidden=${!treeNode.expanded}>
159
+ return html`
158
160
  ${children.map(child => layOutNode(child, treeNode.expanded))}
159
161
  ${treeNode.node instanceof Element
160
162
  ? html`<li
161
163
  ${highlight(treeNode, /* closeTag=*/ true)}
162
164
  role="treeitem"><span part='shadow-xml-view-close-tag'>${'</' + treeNode.node.tagName + '>'}</span
163
165
  ></li>`
164
- : Lit.nothing}
165
- </ul>`;
166
+ : Lit.nothing}`;
166
167
  // clang-format on
167
168
  }
168
169
 
@@ -2,6 +2,9 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
+ // This files is import lit directly
6
+ /* eslint-disable rulesdir/lit-template-result-or-nothing */
7
+
5
8
  import * as i18n from '../../core/i18n/i18n.js';
6
9
  import type * as I18n from '../../third_party/i18n/i18n.js';
7
10
  import * as Lit from '../../third_party/lit/lit.js';
@@ -14,10 +17,10 @@ const {html} = Lit.StaticHtml;
14
17
  */
15
18
  export function i18nTemplate(
16
19
  registeredStrings: I18n.LocalizedStringSet.RegisteredFileStrings, stringId: string,
17
- placeholders: Record<string, Lit.TemplateResult|string>): Lit.TemplateResult {
20
+ placeholders: Record<string, Lit.TemplateResult|string>): Lit.TemplateResult|typeof Lit.nothing {
18
21
  const formatter = registeredStrings.getLocalizedStringSetFor(i18n.DevToolsLocale.DevToolsLocale.instance().locale)
19
22
  .getMessageFormatterFor(stringId);
20
- let result = html``;
23
+ let result: Lit.TemplateResult|typeof Lit.nothing = Lit.nothing;
21
24
  for (const icuElement of formatter.getAst()) {
22
25
  if (icuElement.type === /* argumentElement */ 1) {
23
26
  const placeholderValue = placeholders[icuElement.value];