chrome-devtools-frontend 1.0.1555430 → 1.0.1556696

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 (100) hide show
  1. package/front_end/entrypoints/formatter_worker/FormatterActions.ts +2 -0
  2. package/front_end/entrypoints/formatter_worker/ScopeParser.ts +75 -7
  3. package/front_end/entrypoints/formatter_worker/Substitute.ts +1 -1
  4. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  5. package/front_end/generated/protocol.ts +0 -1
  6. package/front_end/models/ai_assistance/AiConversation.ts +71 -10
  7. package/front_end/models/ai_assistance/ArtifactsManager.ts +67 -0
  8. package/front_end/models/ai_assistance/ConversationHandler.ts +3 -2
  9. package/front_end/models/ai_assistance/agents/AiAgent.ts +17 -27
  10. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +151 -3
  11. package/front_end/models/ai_assistance/agents/StylingAgent.ts +1 -1
  12. package/front_end/models/ai_assistance/ai_assistance.ts +2 -0
  13. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +0 -2
  14. package/front_end/models/annotations/AnnotationRepository.ts +2 -2
  15. package/front_end/models/greendev/Prototypes.ts +56 -0
  16. package/front_end/models/greendev/README.md +5 -0
  17. package/front_end/models/greendev/greendev.ts +5 -0
  18. package/front_end/models/trace/extras/TraceTree.ts +4 -2
  19. package/front_end/models/trace/insights/LCPDiscovery.ts +0 -2
  20. package/front_end/models/trace/types/TraceEvents.ts +0 -1
  21. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +96 -91
  22. package/front_end/panels/ai_assistance/aiAssistancePanel.css +16 -0
  23. package/front_end/panels/ai_assistance/components/ArtifactsViewer.ts +109 -7
  24. package/front_end/panels/ai_assistance/components/ChatView.ts +2 -2
  25. package/front_end/panels/ai_assistance/components/CollapsibleAssistanceContentWidget.ts +7 -8
  26. package/front_end/panels/ai_assistance/components/PerformanceAgentFlameChart.ts +15 -8
  27. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +9 -9
  28. package/front_end/panels/ai_assistance/components/artifactsViewer.css +6 -1
  29. package/front_end/panels/ai_assistance/components/collapsibleAssistanceContentWidget.css +5 -6
  30. package/front_end/panels/application/AppManifestView.ts +263 -205
  31. package/front_end/panels/application/ApplicationPanelSidebar.ts +24 -57
  32. package/front_end/panels/application/OpenedWindowDetailsView.ts +2 -0
  33. package/front_end/panels/application/ServiceWorkersView.ts +2 -0
  34. package/front_end/panels/application/StorageView.ts +1 -0
  35. package/front_end/panels/application/appManifestView.css +48 -0
  36. package/front_end/panels/application/components/ProtocolHandlersView.ts +2 -2
  37. package/front_end/panels/elements/ElementsTreeOutline.ts +1 -1
  38. package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +4 -8
  39. package/front_end/panels/linear_memory_inspector/components/LinearMemoryValueInterpreter.ts +148 -97
  40. package/front_end/panels/linear_memory_inspector/components/LinearMemoryViewer.ts +1 -1
  41. package/front_end/panels/linear_memory_inspector/components/linearMemoryValueInterpreter.css +37 -35
  42. package/front_end/panels/settings/SettingsScreen.ts +133 -1
  43. package/front_end/panels/settings/settings-meta.ts +24 -0
  44. package/front_end/panels/settings/settingsScreen.css +4 -0
  45. package/front_end/panels/sources/UISourceCodeFrame.ts +3 -17
  46. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +2 -1
  47. package/front_end/third_party/acorn/estree-legacy.d.ts +2 -0
  48. package/front_end/third_party/chromium/README.chromium +1 -1
  49. package/front_end/third_party/puppeteer/README.chromium +2 -2
  50. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/CDPSession.d.ts.map +1 -1
  51. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/CDPSession.js.map +1 -1
  52. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/ElementHandle.d.ts.map +1 -1
  53. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/ElementHandle.js.map +1 -1
  54. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Frame.d.ts.map +1 -1
  55. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Frame.js.map +1 -1
  56. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts.map +1 -1
  57. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js.map +1 -1
  58. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Connection.d.ts.map +1 -1
  59. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/BrowserConnector.js +21 -7
  60. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/BrowserConnector.js.map +1 -1
  61. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/common/EventEmitter.d.ts.map +1 -1
  62. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  63. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  64. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  65. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  66. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +15 -6
  67. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/CDPSession.d.ts.map +1 -1
  68. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/CDPSession.js.map +1 -1
  69. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/ElementHandle.d.ts.map +1 -1
  70. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/ElementHandle.js.map +1 -1
  71. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Frame.d.ts.map +1 -1
  72. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Frame.js.map +1 -1
  73. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts.map +1 -1
  74. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js.map +1 -1
  75. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/Connection.d.ts.map +1 -1
  76. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/BrowserConnector.js +21 -7
  77. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/BrowserConnector.js.map +1 -1
  78. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/common/EventEmitter.d.ts.map +1 -1
  79. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  80. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  81. package/front_end/third_party/puppeteer/package/package.json +2 -2
  82. package/front_end/third_party/puppeteer/package/src/api/CDPSession.ts +1 -2
  83. package/front_end/third_party/puppeteer/package/src/api/ElementHandle.ts +2 -4
  84. package/front_end/third_party/puppeteer/package/src/api/Frame.ts +2 -4
  85. package/front_end/third_party/puppeteer/package/src/api/Page.ts +2 -4
  86. package/front_end/third_party/puppeteer/package/src/bidi/core/Connection.ts +3 -2
  87. package/front_end/third_party/puppeteer/package/src/common/BrowserConnector.ts +29 -10
  88. package/front_end/third_party/puppeteer/package/src/common/EventEmitter.ts +3 -3
  89. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  90. package/front_end/ui/components/report_view/ReportView.docs.ts +37 -0
  91. package/front_end/ui/components/report_view/ReportView.ts +1 -4
  92. package/front_end/ui/components/settings/SettingCheckbox.ts +1 -1
  93. package/front_end/ui/legacy/Floaty.ts +5 -9
  94. package/front_end/ui/legacy/InspectorView.ts +2 -1
  95. package/front_end/ui/legacy/ReportView.ts +5 -4
  96. package/front_end/ui/legacy/Widget.ts +7 -0
  97. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +0 -1
  98. package/front_end/ui/legacy/reportView.css +0 -24
  99. package/front_end/ui/visual_logging/KnownContextValues.ts +7 -0
  100. package/package.json +1 -1
@@ -14,6 +14,7 @@ import * as Protocol from '../../generated/protocol.js';
14
14
  import * as AiAssistanceModel from '../../models/ai_assistance/ai_assistance.js';
15
15
  import * as Annotations from '../../models/annotations/annotations.js';
16
16
  import * as Badges from '../../models/badges/badges.js';
17
+ import * as GreenDev from '../../models/greendev/greendev.js';
17
18
  import * as TextUtils from '../../models/text_utils/text_utils.js';
18
19
  import * as Workspace from '../../models/workspace/workspace.js';
19
20
  import * as Buttons from '../../ui/components/buttons/buttons.js';
@@ -225,10 +226,10 @@ function selectedElementFilter(maybeNode: SDK.DOMModel.DOMNode|null): SDK.DOMMod
225
226
  return null;
226
227
  }
227
228
 
228
- async function getEmptyStateSuggestions(
229
- context: AiAssistanceModel.AiAgent.ConversationContext<unknown>|null,
230
- conversation?: AiAssistanceModel.AiConversation.AiConversation):
229
+ async function getEmptyStateSuggestions(conversation?: AiAssistanceModel.AiConversation.AiConversation):
231
230
  Promise<AiAssistanceModel.AiAgent.ConversationSuggestion[]> {
231
+ const context = conversation?.selectedContext;
232
+
232
233
  if (context) {
233
234
  const specialSuggestions = await context.getSuggestions();
234
235
 
@@ -271,9 +272,10 @@ async function getEmptyStateSuggestions(
271
272
  }
272
273
  }
273
274
 
274
- function getMarkdownRenderer(
275
- context: AiAssistanceModel.AiAgent.ConversationContext<unknown>|null,
276
- conversation?: AiAssistanceModel.AiConversation.AiConversation): MarkdownRendererWithCodeBlock {
275
+ function getMarkdownRenderer(conversation?: AiAssistanceModel.AiConversation.AiConversation):
276
+ MarkdownRendererWithCodeBlock {
277
+ const context = conversation?.selectedContext;
278
+
277
279
  if (context instanceof AiAssistanceModel.PerformanceAgent.PerformanceTraceContext) {
278
280
  if (!context.external) {
279
281
  const focus = context.getItem();
@@ -390,7 +392,7 @@ function toolbarView(input: ToolbarViewInput): Lit.LitTemplate {
390
392
  .variant=${Buttons.Button.Variant.TOOLBAR}
391
393
  @click=${input.onSettingsClick}></devtools-button>
392
394
  <!-- If the green experiment is enabled, render the artifacts sidebar toggle button -->
393
- ${Root.Runtime.hostConfig.devToolsGreenDevUi?.enabled ? html`<devtools-button
395
+ ${GreenDev.Prototypes.instance().isEnabled('artifactViewer') ? html`<devtools-button
394
396
  title=${i18nString(UIStrings.settings)}
395
397
  aria-label=${i18nString(UIStrings.settings)}
396
398
  .iconName=${input.artifactsSidebarVisible ? 'left-panel-open' : 'left-panel-close'}
@@ -407,7 +409,7 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
407
409
  function renderState(): Lit.TemplateResult {
408
410
  switch (input.state) {
409
411
  case ViewState.CHAT_VIEW: {
410
- const aiChatView = html`
412
+ return html`
411
413
  <devtools-ai-chat-view
412
414
  .props=${input.props}
413
415
  ${Lit.Directives.ref((el: Element | undefined) => {
@@ -418,29 +420,6 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
418
420
  output.chatView = el;
419
421
  })}
420
422
  ></devtools-ai-chat-view>`;
421
- // If the green experiment is enabled, render the chat view inside
422
- // a split view to also have an artifacts viewer sidebar.
423
- if(Root.Runtime.hostConfig.devToolsGreenDevUi?.enabled) {
424
- return html`
425
- <devtools-split-view
426
- direction="column"
427
- sidebar-visibility=${input.props.isArtifactsSidebarOpen ? 'visible' : 'hidden'}
428
- sidebar-position="second"
429
- style="width: 100%;"
430
- >
431
- <div slot="main">
432
- ${aiChatView}
433
- </div>
434
- <div slot="sidebar">
435
- <devtools-widget
436
- class="fill-panel"
437
- .widgetConfig=${UI.Widget.widgetConfig(ArtifactsViewer)}
438
- ></devtools-widget>
439
- </div>
440
- </devtools-split-view>`;
441
- }
442
-
443
- return aiChatView;
444
423
  }
445
424
  case ViewState.EXPLORE_VIEW:
446
425
  return html`<devtools-widget
@@ -456,13 +435,40 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
456
435
  }
457
436
  }
458
437
 
459
- Lit.render(
460
- html`
461
- ${toolbarView(input)}
462
- <div class="ai-assistance-view-container">${renderState()}</div>
463
- `,
464
- target
465
- );
438
+ const panelWithToolbar = html`
439
+ ${toolbarView(input)}
440
+ <div class="ai-assistance-view-container">${renderState()}</div>`;
441
+
442
+ // If the green experiment is enabled, render the chat view inside
443
+ // a split view to also have an artifacts viewer sidebar.
444
+ if(GreenDev.Prototypes.instance().isEnabled('artifactViewer')) {
445
+ Lit.render(
446
+ html`
447
+ <devtools-split-view
448
+ direction="column"
449
+ sidebar-visibility=${input.artifactsSidebarVisible ? 'visible' : 'hidden'}
450
+ sidebar-position="second"
451
+ sidebar-initial-size="520"
452
+ style="width: 100%;">
453
+ <div slot="main" class="assistance-view-wrapper-with-sidebar">
454
+ ${panelWithToolbar}
455
+ </div>
456
+ <div slot="sidebar">
457
+ <div class="artifacts-toolbar-container" role="toolbar">
458
+ <div>Artifacts Viewer</div>
459
+ </div>
460
+ <devtools-widget
461
+ class="fill-panel"
462
+ .widgetConfig=${UI.Widget.widgetConfig(ArtifactsViewer)}
463
+ ></devtools-widget>
464
+ </div>
465
+ </devtools-split-view>
466
+ `,
467
+ target
468
+ );
469
+ } else {
470
+ Lit.render(panelWithToolbar, target);
471
+ }
466
472
  // clang-format on
467
473
  }
468
474
 
@@ -523,12 +529,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
523
529
 
524
530
  // Whether the UI should show loading or not.
525
531
  #isLoading = false;
526
- // Selected conversation context. The reason we keep this as a
527
- // state field rather than using `#getConversationContext` is that,
528
- // there is a case where the context differs from the selectedElement (or other selected context type).
529
- // Specifically, it allows restoring the previous context when a new selection is cross-origin.
530
- // See `#onContextSelectionChanged` for details.
531
- #selectedContext: AiAssistanceModel.AiAgent.ConversationContext<unknown>|null = null;
532
532
  // Stores the availability status of the `AidaClient` and the reason for unavailability, if any.
533
533
  #aidaAvailability: Host.AidaClient.AidaAccessPreconditions;
534
534
  // Info of the currently logged in user.
@@ -565,6 +565,10 @@ export class AiAssistancePanel extends UI.Panel.Panel {
565
565
  }
566
566
  AiAssistanceModel.AiHistoryStorage.AiHistoryStorage.instance().addEventListener(
567
567
  AiAssistanceModel.AiHistoryStorage.Events.HISTORY_DELETED, this.#onHistoryDeleted, this);
568
+ if (GreenDev.Prototypes.instance().isEnabled('artifactViewer')) {
569
+ AiAssistanceModel.ArtifactsManager.ArtifactsManager.instance().addEventListener(
570
+ AiAssistanceModel.ArtifactsManager.ArtifactAddedEvent.eventName, this.#onArtifactAdded.bind(this));
571
+ }
568
572
  }
569
573
 
570
574
  async #getPanelViewInput(): Promise<PanelViewInput> {
@@ -581,16 +585,16 @@ export class AiAssistancePanel extends UI.Panel.Panel {
581
585
  }
582
586
 
583
587
  if (this.#conversation) {
584
- const emptyStateSuggestions = await getEmptyStateSuggestions(this.#selectedContext, this.#conversation);
585
- const markdownRenderer = getMarkdownRenderer(this.#selectedContext, this.#conversation);
588
+ const emptyStateSuggestions = await getEmptyStateSuggestions(this.#conversation);
589
+ const markdownRenderer = getMarkdownRenderer(this.#conversation);
586
590
  return {
587
591
  state: ViewState.CHAT_VIEW,
588
592
  props: {
589
593
  additionalFloatyContext: this.#additionalContextItemsFromFloaty,
590
- blockedByCrossOrigin: this.#blockedByCrossOrigin,
594
+ blockedByCrossOrigin: this.#conversation.isBlockedByOrigin,
591
595
  isLoading: this.#isLoading,
592
596
  messages: this.#messages,
593
- selectedContext: this.#selectedContext,
597
+ selectedContext: this.#conversation.selectedContext ?? null,
594
598
  conversationType: this.#conversation.type,
595
599
  isReadOnly: this.#conversation.isReadOnly ?? false,
596
600
  changeSummary: this.#getChangeSummary(),
@@ -762,6 +766,8 @@ export class AiAssistancePanel extends UI.Panel.Panel {
762
766
  this.#conversation = conversation;
763
767
  }
764
768
 
769
+ this.#conversation?.setContext(this.#getConversationContext(this.#conversation));
770
+
765
771
  this.requestUpdate();
766
772
  }
767
773
 
@@ -814,12 +820,19 @@ export class AiAssistancePanel extends UI.Panel.Panel {
814
820
 
815
821
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistancePanelOpened);
816
822
 
817
- if (Root.Runtime.hostConfig.devToolsGreenDevUi?.enabled) {
823
+ if (GreenDev.Prototypes.instance().isEnabled('inDevToolsFloaty')) {
818
824
  UI.Context.Context.instance().addFlavorChangeListener(UI.Floaty.FloatyFlavor, this.#bindFloatyListener, this);
819
825
  this.#bindFloatyListener();
820
826
  }
821
827
  }
822
828
 
829
+ #onArtifactAdded(): void {
830
+ if (AiAssistanceModel.ArtifactsManager.ArtifactsManager.instance().artifacts.length > 0) {
831
+ this.#isArtifactsSidebarOpen = true;
832
+ this.requestUpdate();
833
+ }
834
+ }
835
+
823
836
  override willHide(): void {
824
837
  super.willHide();
825
838
  this.#aiAssistanceEnabledSetting?.removeChangeListener(this.requestUpdate, this);
@@ -999,13 +1012,15 @@ export class AiAssistancePanel extends UI.Panel.Panel {
999
1012
  #isTextInputDisabled(): boolean {
1000
1013
  // If sending a new message is blocked by cross origin context
1001
1014
  // the text input is disabled.
1002
- if (this.#blockedByCrossOrigin) {
1015
+ if (this.#conversation && this.#conversation.isBlockedByOrigin) {
1003
1016
  return true;
1004
1017
  }
1005
1018
 
1006
- // If there is no current agent if there is no selected context
1007
- // the text input is disabled.
1008
- if (!this.#conversation || !this.#selectedContext) {
1019
+ if (!this.#conversation) {
1020
+ return true;
1021
+ }
1022
+
1023
+ if (!this.#conversation.selectedContext) {
1009
1024
  return true;
1010
1025
  }
1011
1026
 
@@ -1030,24 +1045,27 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1030
1045
  return i18nString(UIStrings.followTheSteps);
1031
1046
  }
1032
1047
 
1033
- if (this.#blockedByCrossOrigin) {
1048
+ if (this.#conversation && this.#conversation.isBlockedByOrigin) {
1034
1049
  return lockedString(UIStringsNotTranslate.crossOriginError);
1035
1050
  }
1036
1051
 
1037
1052
  switch (this.#conversation.type) {
1038
1053
  case AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING:
1039
- return this.#selectedContext ? lockedString(UIStringsNotTranslate.inputPlaceholderForStyling) :
1040
- lockedString(UIStringsNotTranslate.inputPlaceholderForStylingNoContext);
1054
+ return this.#conversation.selectedContext ?
1055
+ lockedString(UIStringsNotTranslate.inputPlaceholderForStyling) :
1056
+ lockedString(UIStringsNotTranslate.inputPlaceholderForStylingNoContext);
1041
1057
  case AiAssistanceModel.AiHistoryStorage.ConversationType.FILE:
1042
- return this.#selectedContext ? lockedString(UIStringsNotTranslate.inputPlaceholderForFile) :
1043
- lockedString(UIStringsNotTranslate.inputPlaceholderForFileNoContext);
1058
+ return this.#conversation.selectedContext ?
1059
+ lockedString(UIStringsNotTranslate.inputPlaceholderForFile) :
1060
+ lockedString(UIStringsNotTranslate.inputPlaceholderForFileNoContext);
1044
1061
  case AiAssistanceModel.AiHistoryStorage.ConversationType.NETWORK:
1045
- return this.#selectedContext ? lockedString(UIStringsNotTranslate.inputPlaceholderForNetwork) :
1046
- lockedString(UIStringsNotTranslate.inputPlaceholderForNetworkNoContext);
1062
+ return this.#conversation.selectedContext ?
1063
+ lockedString(UIStringsNotTranslate.inputPlaceholderForNetwork) :
1064
+ lockedString(UIStringsNotTranslate.inputPlaceholderForNetworkNoContext);
1047
1065
  case AiAssistanceModel.AiHistoryStorage.ConversationType.PERFORMANCE: {
1048
1066
  const perfPanel = UI.Context.Context.instance().flavor(TimelinePanel.TimelinePanel.TimelinePanel);
1049
1067
  if (perfPanel?.hasActiveTrace()) {
1050
- return this.#selectedContext ?
1068
+ return this.#conversation.selectedContext ?
1051
1069
  lockedString(UIStringsNotTranslate.inputPlaceholderForPerformanceTrace) :
1052
1070
  lockedString(UIStringsNotTranslate.inputPlaceholderForPerformanceTraceNoContext);
1053
1071
  }
@@ -1107,7 +1125,11 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1107
1125
  }
1108
1126
 
1109
1127
  #handleContextClick(): void|Promise<void> {
1110
- const context = this.#selectedContext;
1128
+ if (!this.#conversation) {
1129
+ return;
1130
+ }
1131
+
1132
+ const context = this.#conversation.selectedContext;
1111
1133
  if (context instanceof AiAssistanceModel.NetworkAgent.RequestContext) {
1112
1134
  const requestLocation = NetworkForward.UIRequestLocation.UIRequestLocation.tab(
1113
1135
  context.getItem(), NetworkForward.UIRequestLocation.UIRequestTabs.HEADERS_COMPONENT);
@@ -1205,7 +1227,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1205
1227
  this.#imageInput = undefined;
1206
1228
  this.#isTextInputEmpty = true;
1207
1229
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceQuerySubmitted);
1208
- if (this.#blockedByCrossOrigin) {
1230
+ if (this.#conversation && this.#conversation.isBlockedByOrigin) {
1209
1231
  this.#handleNewChatRequest();
1210
1232
  }
1211
1233
  await this.#startConversation(predefinedPrompt);
@@ -1399,41 +1421,21 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1399
1421
  this.#runAbortController = new AbortController();
1400
1422
  }
1401
1423
 
1402
- // Indicates whether the new conversation context is blocked due to cross-origin restrictions.
1403
- // This happens when the conversation's context has a different
1404
- // origin than the selected context.
1405
- get #blockedByCrossOrigin(): boolean {
1406
- if (!this.#conversation) {
1407
- return false;
1408
- }
1409
- this.#selectedContext = this.#getConversationContext(this.#conversation);
1410
- if (!this.#selectedContext) {
1411
- return false;
1412
- }
1413
- return !this.#selectedContext.isOriginAllowed(this.#conversation.origin);
1414
- }
1415
-
1416
1424
  #getConversationContext(conversation?: AiAssistanceModel.AiConversation.AiConversation):
1417
1425
  AiAssistanceModel.AiAgent.ConversationContext<unknown>|null {
1418
1426
  if (!conversation) {
1419
1427
  return null;
1420
1428
  }
1421
- let context: AiAssistanceModel.AiAgent.ConversationContext<unknown>|null;
1422
1429
  switch (conversation.type) {
1423
1430
  case AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING:
1424
- context = this.#selectedElement;
1425
- break;
1431
+ return this.#selectedElement;
1426
1432
  case AiAssistanceModel.AiHistoryStorage.ConversationType.FILE:
1427
- context = this.#selectedFile;
1428
- break;
1433
+ return this.#selectedFile;
1429
1434
  case AiAssistanceModel.AiHistoryStorage.ConversationType.NETWORK:
1430
- context = this.#selectedRequest;
1431
- break;
1435
+ return this.#selectedRequest;
1432
1436
  case AiAssistanceModel.AiHistoryStorage.ConversationType.PERFORMANCE:
1433
- context = this.#selectedPerformanceTrace;
1434
- break;
1437
+ return this.#selectedPerformanceTrace;
1435
1438
  }
1436
- return context;
1437
1439
  }
1438
1440
 
1439
1441
  async #startConversation(
@@ -1448,8 +1450,10 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1448
1450
  this.#cancel();
1449
1451
  const signal = this.#runAbortController.signal;
1450
1452
  const context = this.#getConversationContext(this.#conversation);
1453
+ this.#conversation.setContext(context);
1454
+
1451
1455
  // If a different context is provided, it must be from the same origin.
1452
- if (context && !context.isOriginAllowed(this.#conversation.origin)) {
1456
+ if (this.#conversation.isBlockedByOrigin) {
1453
1457
  // This error should not be reached. If it happens, some
1454
1458
  // invariants do not hold anymore.
1455
1459
  throw new Error('cross-origin context data should not be included');
@@ -1468,12 +1472,13 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1468
1472
 
1469
1473
  await this.#doConversation(
1470
1474
  this.#conversation.run(
1471
- text, {
1475
+ text,
1476
+ {
1472
1477
  signal,
1473
- selected: context,
1474
1478
  extraContext: this.#additionalContextItemsFromFloaty,
1479
+ multimodalInput,
1475
1480
  },
1476
- multimodalInput),
1481
+ ),
1477
1482
  );
1478
1483
  }
1479
1484
 
@@ -13,6 +13,22 @@
13
13
  justify-content: space-between;
14
14
  }
15
15
 
16
+ .artifacts-toolbar-container {
17
+ display: flex;
18
+ align-items: center;
19
+ justify-content: flex-start;
20
+ height: 27px;
21
+ padding: 5px;
22
+ border-bottom: 1px solid var(--sys-color-divider);
23
+ }
24
+
25
+ .assistance-view-wrapper-with-sidebar {
26
+ display: flex;
27
+ flex-direction: column;
28
+ height: 100%;
29
+ overflow: hidden;
30
+ }
31
+
16
32
  .ai-assistance-view-container {
17
33
  display: flex;
18
34
  flex-direction: column;
@@ -2,19 +2,92 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
+ import './CollapsibleAssistanceContentWidget.js';
6
+ import './PerformanceAgentFlameChart.js';
7
+
8
+ import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
9
+ import * as Logs from '../../../models/logs/logs.js';
10
+ import * as NetworkTimeCalculator from '../../../models/network_time_calculator/network_time_calculator.js';
11
+ import * as Trace from '../../../models/trace/trace.js';
5
12
  import * as UI from '../../../ui/legacy/legacy.js';
6
13
  import * as Lit from '../../../ui/lit/lit.js';
14
+ import * as Network from '../../network/network.js';
15
+ import * as Insights from '../../timeline/components/insights/insights.js';
7
16
 
8
17
  import artifactsViewerStyles from './artifactsViewer.css.js';
18
+ import type * as PerformanceAgentFlameChart from './PerformanceAgentFlameChart.js';
9
19
 
10
20
  const {html, render} = Lit;
11
21
 
12
22
  export interface ViewInput {
13
- artifacts: [];
23
+ artifacts: AiAssistanceModel.ArtifactsManager.Artifact[];
24
+ parsedTrace: Trace.TraceModel.ParsedTrace;
25
+ }
26
+
27
+ export function renderArtifact(
28
+ artifact: AiAssistanceModel.ArtifactsManager.Artifact, parsedTrace: Trace.TraceModel.ParsedTrace): Lit.LitTemplate {
29
+ switch (artifact.type) {
30
+ // clang-format off
31
+ case 'insight': {
32
+ const insightRenderer = new Insights.InsightRenderer.InsightRenderer();
33
+ const componentName = artifact.insightType;
34
+ const insightSet = parsedTrace.insights?.values().next().value;
35
+ const insightModel = insightSet?.model[componentName as Trace.Insights.Types.InsightKeys];
36
+
37
+ if (!insightModel) {
38
+ return Lit.nothing;
39
+ }
40
+
41
+ return html`
42
+ <devtools-collapsible-assistance-content-widget .data=${{headerText: `Insight - ${componentName}`}}>
43
+ ${insightRenderer.renderInsightToWidgetElement(parsedTrace, insightSet, insightModel, componentName, {
44
+ selected: true,
45
+ isAIAssistanceContext: true,
46
+ })}
47
+ </devtools-collapsible-assistance-content-widget>`;
48
+ }
49
+ case 'network-request': {
50
+ const networkRequest = artifact.request;
51
+ if ('args' in networkRequest && Trace.Types.Events.isSyntheticNetworkRequest(networkRequest)) {
52
+ const calculator = new NetworkTimeCalculator.NetworkTimeCalculator(true);
53
+ const sdkRequest = Logs.NetworkLog.NetworkLog.instance()
54
+ .requestsForId(networkRequest.args.data.requestId)
55
+ .find(r => r.url() === networkRequest.args.data.url) ??
56
+ null;
57
+ if (!sdkRequest) {
58
+ return Lit.nothing;
59
+ }
60
+ return html`
61
+ <devtools-collapsible-assistance-content-widget
62
+ .data=${{headerText: `Network Request: ${
63
+ sdkRequest.url().length > 80 ? sdkRequest.url().slice(0, 80) + '...' : sdkRequest.url()}`}}>
64
+ <devtools-widget class="actions" .widgetConfig=${UI.Widget.widgetConfig(Network.RequestTimingView.RequestTimingView, {
65
+ request: sdkRequest,
66
+ calculator,
67
+ })}></devtools-widget>
68
+ </devtools-collapsible-assistance-content-widget>`;
69
+ }
70
+ return Lit.nothing;
71
+ }
72
+ case 'flamechart': {
73
+ return html`
74
+ <devtools-collapsible-assistance-content-widget .data=${{headerText: `Flamechart`}}>
75
+ <devtools-performance-agent-flame-chart .data=${{
76
+ parsedTrace,
77
+ start: artifact.start,
78
+ end: artifact.end,
79
+ } as PerformanceAgentFlameChart.PerformanceAgentFlameChartData}>
80
+ </devtools-performance-agent-flame-chart>
81
+ </devtools-collapsible-assistance-content-widget>`;
82
+ }
83
+ default:
84
+ return Lit.nothing;
85
+ // clang-format on
86
+ }
14
87
  }
15
88
 
16
89
  export const DEFAULT_VIEW = (
17
- _input: ViewInput,
90
+ input: ViewInput,
18
91
  _output: Record<string, unknown>,
19
92
  target: HTMLElement,
20
93
  ): void => {
@@ -22,8 +95,8 @@ export const DEFAULT_VIEW = (
22
95
  render(
23
96
  html`
24
97
  <style>${artifactsViewerStyles}</style>
25
- <div>
26
- Artifacts Viewer
98
+ <div class="artifacts-viewer">
99
+ ${input.artifacts.map(artifact => renderArtifact(artifact, input.parsedTrace))}
27
100
  </div>
28
101
  `,
29
102
  target
@@ -35,20 +108,49 @@ export type View = typeof DEFAULT_VIEW;
35
108
 
36
109
  export class ArtifactsViewer extends UI.Widget.Widget {
37
110
  #view: View;
111
+ #parsedTrace: Trace.TraceModel.ParsedTrace|null;
38
112
  constructor(element?: HTMLElement, view = DEFAULT_VIEW) {
39
113
  super(element);
40
114
  this.#view = view;
115
+ this.#parsedTrace = null;
41
116
  }
42
117
 
43
118
  override wasShown(): void {
44
119
  super.wasShown();
45
- void this.requestUpdate();
120
+ AiAssistanceModel.ArtifactsManager.ArtifactsManager.instance().addEventListener(
121
+ AiAssistanceModel.ArtifactsManager.ArtifactAddedEvent.eventName,
122
+ () => {
123
+ if (this.#parsedTrace) {
124
+ this.performUpdate();
125
+ }
126
+ },
127
+ );
128
+
129
+ UI.Context.Context.instance().addFlavorChangeListener(
130
+ AiAssistanceModel.AIContext.AgentFocus,
131
+ ({data}) => {
132
+ this.#parsedTrace = data.parsedTrace;
133
+ if (this.#parsedTrace) {
134
+ this.performUpdate();
135
+ }
136
+ },
137
+ );
138
+
139
+ const focus = UI.Context.Context.instance().flavor(AiAssistanceModel.AIContext.AgentFocus);
140
+ if (focus) {
141
+ this.#parsedTrace = focus.parsedTrace;
142
+ this.performUpdate();
143
+ }
46
144
  }
47
145
 
48
- override performUpdate(): Promise<void>|void {
146
+ override performUpdate(): void {
147
+ if (!this.#parsedTrace) {
148
+ return;
149
+ }
49
150
  this.#view(
50
151
  {
51
- artifacts: [],
152
+ artifacts: AiAssistanceModel.ArtifactsManager.ArtifactsManager.instance().artifacts,
153
+ parsedTrace: this.#parsedTrace,
52
154
  },
53
155
  {},
54
156
  this.contentElement,
@@ -9,9 +9,9 @@ import '../../../ui/components/spinners/spinners.js';
9
9
  import * as Host from '../../../core/host/host.js';
10
10
  import * as i18n from '../../../core/i18n/i18n.js';
11
11
  import * as Platform from '../../../core/platform/platform.js';
12
- import * as Root from '../../../core/root/root.js';
13
12
  import * as SDK from '../../../core/sdk/sdk.js';
14
13
  import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
14
+ import * as GreenDev from '../../../models/greendev/greendev.js';
15
15
  import * as Trace from '../../../models/trace/trace.js';
16
16
  import * as Workspace from '../../../models/workspace/workspace.js';
17
17
  import * as PanelsCommon from '../../../panels/common/common.js';
@@ -1472,7 +1472,7 @@ function renderChatInput({
1472
1472
  }
1473
1473
 
1474
1474
  function renderFloatyExtraContext(contexts: UI.Floaty.FloatyContextSelection[]): Lit.LitTemplate {
1475
- if (!Root.Runtime.hostConfig.devToolsGreenDevUi?.enabled) {
1475
+ if (!GreenDev.Prototypes.instance().isEnabled('inDevToolsFloaty')) {
1476
1476
  return Lit.nothing;
1477
1477
  }
1478
1478
 
@@ -16,7 +16,7 @@ export interface CollapsibleAssistanceContentWidgetData {
16
16
 
17
17
  export class CollapsibleAssistanceContentWidget extends HTMLElement {
18
18
  readonly #shadow = this.attachShadow({mode: 'open'});
19
- #isCollapsed = true;
19
+ #isCollapsed = false;
20
20
  #headerText = 'Details';
21
21
 
22
22
  set data(data: CollapsibleAssistanceContentWidgetData) {
@@ -42,16 +42,15 @@ export class CollapsibleAssistanceContentWidget extends HTMLElement {
42
42
  event.preventDefault();
43
43
  this.#toggleCollapse();
44
44
  }}>
45
+ ${this.#headerText}
45
46
  <devtools-button .data=${{
46
47
  variant: Buttons.Button.Variant.ICON,
47
- iconName: this.#isCollapsed ? 'triangle-right' : 'triangle-down',
48
- color: 'var(--sys-color-on-surface)',
49
- width: '14px',
50
- height: '14px',
48
+ iconName: this.#isCollapsed ? 'triangle-right' : 'triangle-down',
49
+ color: 'var(--sys-color-on-surface)',
50
+ width: '14px',
51
+ height: '14px',
51
52
  } as Buttons.Button.ButtonData}
52
- >
53
- </devtools-button>
54
- ${this.#headerText}
53
+ ></devtools-button>
55
54
  </summary>
56
55
  <div class="content">
57
56
  <slot></slot>
@@ -11,8 +11,8 @@ const {html} = Lit;
11
11
 
12
12
  export interface PerformanceAgentFlameChartData {
13
13
  parsedTrace: Trace.TraceModel.ParsedTrace|null;
14
- start: number;
15
- end: number;
14
+ start: Trace.Types.Timing.Micro;
15
+ end: Trace.Types.Timing.Micro;
16
16
  }
17
17
 
18
18
  export class PerformanceAgentFlameChart extends HTMLElement implements PerfUI.FlameChart.FlameChartDelegate {
@@ -30,6 +30,8 @@ export class PerformanceAgentFlameChart extends HTMLElement implements PerfUI.Fl
30
30
  this.#flameChart = new PerfUI.FlameChart.FlameChart(this.#dataProvider, this);
31
31
  this.#flameChart.markAsRoot();
32
32
  this.#flameChart.show(this.#flameChartContainer);
33
+ const observer = new ResizeObserver(this.#onResize.bind(this));
34
+ observer.observe(this.#flameChartContainer);
33
35
  }
34
36
 
35
37
  set data(data: PerformanceAgentFlameChartData) {
@@ -59,15 +61,20 @@ export class PerformanceAgentFlameChart extends HTMLElement implements PerfUI.Fl
59
61
  }
60
62
 
61
63
  const bounds = Trace.Helpers.Timing.traceWindowMicroSecondsToMilliSeconds({
62
- min: Trace.Types.Timing.Micro(start),
63
- max: Trace.Types.Timing.Micro(end),
64
+ min: start,
65
+ max: end,
64
66
  range: Trace.Types.Timing.Micro(end - start),
65
67
  });
66
68
  this.#flameChart.setWindowTimes(bounds.min, bounds.max);
67
- this.#flameChart.setSize(600, 200);
69
+ this.#flameChart.setSize(600, 300);
68
70
  this.#render();
69
71
  }
70
72
 
73
+ #onResize(entries: ResizeObserverEntry[]): void {
74
+ const container = entries[0];
75
+ this.#flameChart.setSize(container.contentRect.width, 600);
76
+ }
77
+
71
78
  #render(): void {
72
79
  if (!this.#parsedTrace) {
73
80
  return;
@@ -80,8 +87,8 @@ export class PerformanceAgentFlameChart extends HTMLElement implements PerfUI.Fl
80
87
 
81
88
  .container {
82
89
  display: flex;
83
- width: 600px;
84
- height: 200px;
90
+ width: 100%;
91
+ height: 300px;
85
92
  }
86
93
 
87
94
  .flex-auto {
@@ -100,7 +107,7 @@ export class PerformanceAgentFlameChart extends HTMLElement implements PerfUI.Fl
100
107
  Lit.render(output, this.#shadow, {host: this});
101
108
 
102
109
  this.#flameChart.update();
103
- this.#flameChart.setSize(600, 200);
110
+ this.#flameChart.setSize(600, 300);
104
111
  }
105
112
  windowChanged(startTime: number, endTime: number, animate: boolean): void {
106
113
  this.#flameChart.setWindowTimes(startTime, endTime, animate);