chrome-devtools-frontend 1.0.1559913 → 1.0.1561080

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 (130) hide show
  1. package/front_end/core/host/InspectorFrontendHostStub.ts +0 -3
  2. package/front_end/core/platform/ArrayUtilities.ts +13 -0
  3. package/front_end/core/root/Runtime.ts +0 -5
  4. package/front_end/core/sdk/DOMModel.ts +8 -0
  5. package/front_end/core/sdk/NetworkManager.ts +4 -0
  6. package/front_end/core/sdk/NetworkRequest.ts +9 -0
  7. package/front_end/core/sdk/OverlayModel.ts +20 -9
  8. package/front_end/entrypoints/main/MainImpl.ts +2 -1
  9. package/front_end/generated/InspectorBackendCommands.ts +4 -2
  10. package/front_end/generated/protocol-mapping.d.ts +7 -0
  11. package/front_end/generated/protocol-proxy-api.d.ts +5 -0
  12. package/front_end/generated/protocol.ts +24 -0
  13. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +23 -22
  14. package/front_end/models/badges/UserBadges.ts +48 -16
  15. package/front_end/models/greendev/Prototypes.ts +6 -1
  16. package/front_end/models/trace/extras/TraceTree.ts +1 -1
  17. package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +8 -3
  18. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +11 -142
  19. package/front_end/panels/ai_assistance/PatchWidget.ts +90 -72
  20. package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
  21. package/front_end/panels/ai_assistance/components/ChatInput.ts +701 -0
  22. package/front_end/panels/ai_assistance/components/ChatView.ts +71 -1268
  23. package/front_end/panels/ai_assistance/components/UserActionRow.ts +514 -31
  24. package/front_end/panels/ai_assistance/components/chatInput.css +387 -0
  25. package/front_end/panels/ai_assistance/components/chatView.css +38 -599
  26. package/front_end/panels/ai_assistance/components/userActionRow.css +230 -0
  27. package/front_end/panels/autofill/AutofillView.ts +2 -2
  28. package/front_end/panels/changes/ChangesView.ts +15 -1
  29. package/front_end/panels/changes/changesView.css +6 -0
  30. package/front_end/panels/common/BadgeNotification.ts +44 -58
  31. package/front_end/panels/common/CopyChangesToPrompt.ts +233 -0
  32. package/front_end/panels/common/common.ts +1 -0
  33. package/front_end/panels/elements/ElementsTreeElement.ts +183 -359
  34. package/front_end/panels/elements/ElementsTreeOutline.ts +0 -6
  35. package/front_end/panels/elements/ShortcutTreeElement.ts +57 -50
  36. package/front_end/panels/elements/StylePropertiesSection.ts +1 -3
  37. package/front_end/panels/elements/components/AdornerManager.ts +5 -149
  38. package/front_end/panels/issues/HiddenIssuesRow.ts +1 -2
  39. package/front_end/panels/issues/IssueKindView.ts +2 -4
  40. package/front_end/panels/issues/IssueView.ts +2 -4
  41. package/front_end/panels/network/NetworkDataGridNode.ts +65 -1
  42. package/front_end/panels/network/NetworkLogView.ts +2 -4
  43. package/front_end/panels/network/NetworkLogViewColumns.ts +9 -0
  44. package/front_end/panels/screencast/ScreencastApp.ts +1 -0
  45. package/front_end/panels/settings/SettingsScreen.ts +3 -2
  46. package/front_end/panels/timeline/CompatibilityTracksAppender.ts +14 -1
  47. package/front_end/panels/timeline/ThirdPartyTreeView.ts +1 -4
  48. package/front_end/panels/timeline/TimelineController.ts +185 -3
  49. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +52 -25
  50. package/front_end/panels/timeline/TimelineFlameChartView.ts +1 -0
  51. package/front_end/panels/timeline/TimelinePanel.ts +17 -104
  52. package/front_end/panels/timeline/TimelineTreeView.ts +1 -0
  53. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +2 -2
  54. package/front_end/panels/timeline/components/insights/Table.ts +3 -3
  55. package/front_end/panels/whats_new/ReleaseNoteText.ts +15 -9
  56. package/front_end/panels/whats_new/resources/WNDT.md +6 -6
  57. package/front_end/third_party/chromium/README.chromium +1 -1
  58. package/front_end/third_party/codemirror.next/rebuild.sh +1 -1
  59. package/front_end/third_party/lit/rebuild.sh +1 -1
  60. package/front_end/third_party/puppeteer/README.chromium +2 -2
  61. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts +2 -3
  62. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts.map +1 -1
  63. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js.map +1 -1
  64. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPRequest.d.ts.map +1 -1
  65. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPRequest.js +9 -0
  66. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPRequest.js.map +1 -1
  67. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPResponse.d.ts +3 -0
  68. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPResponse.d.ts.map +1 -1
  69. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPResponse.js +9 -0
  70. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPResponse.js.map +1 -1
  71. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Request.d.ts +3 -0
  72. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Request.d.ts.map +1 -1
  73. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Request.js +10 -0
  74. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Request.js.map +1 -1
  75. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts.map +1 -1
  76. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js +8 -4
  77. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js.map +1 -1
  78. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.d.ts +1 -1
  79. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.d.ts.map +1 -1
  80. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.js +1 -1
  81. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.js.map +1 -1
  82. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  83. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
  84. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
  85. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  86. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  87. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  88. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  89. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +10 -1
  90. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +13 -7
  91. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts +2 -3
  92. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts.map +1 -1
  93. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js.map +1 -1
  94. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPRequest.d.ts.map +1 -1
  95. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPRequest.js +9 -0
  96. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPRequest.js.map +1 -1
  97. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPResponse.d.ts +3 -0
  98. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPResponse.d.ts.map +1 -1
  99. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPResponse.js +9 -0
  100. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPResponse.js.map +1 -1
  101. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/Request.d.ts +3 -0
  102. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/Request.d.ts.map +1 -1
  103. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/Request.js +10 -0
  104. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/Request.js.map +1 -1
  105. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts.map +1 -1
  106. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js +8 -4
  107. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js.map +1 -1
  108. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.d.ts +1 -1
  109. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.d.ts.map +1 -1
  110. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.js +1 -1
  111. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.js.map +1 -1
  112. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
  113. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
  114. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  115. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  116. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  117. package/front_end/third_party/puppeteer/package/lib/types.d.ts +10 -1
  118. package/front_end/third_party/puppeteer/package/package.json +3 -3
  119. package/front_end/third_party/puppeteer/package/src/api/Page.ts +2 -3
  120. package/front_end/third_party/puppeteer/package/src/bidi/HTTPRequest.ts +13 -0
  121. package/front_end/third_party/puppeteer/package/src/bidi/HTTPResponse.ts +10 -0
  122. package/front_end/third_party/puppeteer/package/src/bidi/core/Request.ts +15 -0
  123. package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +9 -4
  124. package/front_end/third_party/puppeteer/package/src/generated/injected.ts +1 -1
  125. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  126. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  127. package/front_end/ui/components/adorners/Adorner.ts +8 -68
  128. package/front_end/ui/legacy/TabbedPane.ts +1 -1
  129. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  130. package/package.json +2 -1
@@ -10,7 +10,6 @@ import * as i18n from '../../core/i18n/i18n.js';
10
10
  import * as Platform from '../../core/platform/platform.js';
11
11
  import * as Root from '../../core/root/root.js';
12
12
  import * as SDK from '../../core/sdk/sdk.js';
13
- import * as Protocol from '../../generated/protocol.js';
14
13
  import * as AiAssistanceModel from '../../models/ai_assistance/ai_assistance.js';
15
14
  import * as Annotations from '../../models/annotations/annotations.js';
16
15
  import * as Badges from '../../models/badges/badges.js';
@@ -30,28 +29,30 @@ import * as TimelinePanel from '../timeline/timeline.js';
30
29
  import aiAssistancePanelStyles from './aiAssistancePanel.css.js';
31
30
  import {ArtifactsViewer} from './components/ArtifactsViewer.js';
32
31
  import {
33
- type AnswerPart,
34
- type ChatMessage,
35
- ChatMessageEntity,
36
32
  ChatView,
37
- type ImageInputData,
38
- type ModelChatMessage,
39
33
  type Props as ChatViewProps,
40
- type Step
41
34
  } from './components/ChatView.js';
42
35
  import {DisabledWidget} from './components/DisabledWidget.js';
43
36
  import {ExploreWidget} from './components/ExploreWidget.js';
44
37
  import {MarkdownRendererWithCodeBlock} from './components/MarkdownRendererWithCodeBlock.js';
45
38
  import {PerformanceAgentMarkdownRenderer} from './components/PerformanceAgentMarkdownRenderer.js';
39
+ import {
40
+ type AnswerPart,
41
+ type ChatMessage,
42
+ ChatMessageEntity,
43
+ type ModelChatMessage,
44
+ type Step,
45
+ } from './components/UserActionRow.js';
46
46
  import {isAiAssistancePatchingEnabled} from './PatchWidget.js';
47
47
 
48
+ // FIXME: this export is temporary to avoid rewriting tests.
49
+ export {ChatMessageEntity} from './components/UserActionRow.js';
50
+ export type {AnswerPart, ModelChatMessage, StepPart} from './components/UserActionRow.js';
51
+
48
52
  const {html} = Lit;
49
53
 
50
54
  const AI_ASSISTANCE_SEND_FEEDBACK = 'https://crbug.com/364805393' as Platform.DevToolsPath.UrlString;
51
55
  const AI_ASSISTANCE_HELP = 'https://developer.chrome.com/docs/devtools/ai-assistance';
52
- const SCREENSHOT_QUALITY = 100;
53
- const SHOW_LOADING_STATE_TIMEOUT = 100;
54
- const JPEG_MIME_TYPE = 'image/jpeg';
55
56
 
56
57
  const UIStrings = {
57
58
  /**
@@ -204,14 +205,6 @@ const UIStringsNotTranslate = {
204
205
  */
205
206
  inputDisclaimerForPerformanceEnterpriseNoLogging:
206
207
  'Chat messages and data from your performance trace are sent to Google. The content you submit and that is generated by this feature will not be used to improve Google’s AI models. This is an experimental AI feature and won’t always get it right.',
207
- /**
208
- * @description Message displayed in toast in case of any failures while taking a screenshot of the page.
209
- */
210
- screenshotFailureMessage: 'Failed to take a screenshot. Please try again.',
211
- /**
212
- * @description Message displayed in toast in case of any failures while uploading an image file as input.
213
- */
214
- uploadImageFailureMessage: 'Failed to upload image. Please try again.',
215
208
  } as const;
216
209
 
217
210
  const str_ = i18n.i18n.registerUIStrings('panels/ai_assistance/AiAssistancePanel.ts', UIStrings);
@@ -536,9 +529,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
536
529
  accountImage?: string,
537
530
  accountFullName?: string,
538
531
  };
539
- #imageInput?: ImageInputData;
540
- // Used to disable send button when there is not text input.
541
- #isTextInputEmpty = true;
542
532
  #timelinePanelInstance: TimelinePanel.TimelinePanel.TimelinePanel|null = null;
543
533
  #runAbortController = new AbortController();
544
534
  #additionalContextItemsFromFloaty: UI.Floaty.FloatyContextSelection[] = [];
@@ -603,12 +593,10 @@ export class AiAssistancePanel extends UI.Panel.Panel {
603
593
  canShowFeedbackForm: this.#serverSideLoggingEnabled,
604
594
  multimodalInputEnabled: isAiAssistanceMultimodalInputEnabled() &&
605
595
  this.#conversation.type === AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING,
606
- imageInput: this.#imageInput,
607
596
  isTextInputDisabled: this.#isTextInputDisabled(),
608
597
  emptyStateSuggestions,
609
598
  inputPlaceholder: this.#getChatInputPlaceholder(),
610
599
  disclaimerText: this.#getDisclaimerText(),
611
- isTextInputEmpty: this.#isTextInputEmpty,
612
600
  changeManager: this.#changeManager,
613
601
  uploadImageInputEnabled: isAiAssistanceMultimodalUploadInputEnabled() &&
614
602
  this.#conversation.type === AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING,
@@ -617,8 +605,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
617
605
  onTextSubmit: async (
618
606
  text: string, imageInput?: Host.AidaClient.Part,
619
607
  multimodalInputType?: AiAssistanceModel.AiAgent.MultimodalInputType) => {
620
- this.#imageInput = undefined;
621
- this.#isTextInputEmpty = true;
622
608
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceQuerySubmitted);
623
609
  await this.#startConversation(text, imageInput, multimodalInputType);
624
610
  },
@@ -627,12 +613,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
627
613
  onCancelClick: this.#cancel.bind(this),
628
614
  onContextClick: this.#handleContextClick.bind(this),
629
615
  onNewConversation: this.#handleNewChatRequest.bind(this),
630
- onTakeScreenshot: isAiAssistanceMultimodalInputEnabled() ? this.#handleTakeScreenshot.bind(this) : undefined,
631
- onRemoveImageInput: isAiAssistanceMultimodalInputEnabled() ? this.#handleRemoveImageInput.bind(this) :
632
- undefined,
633
616
  onCopyResponseClick: this.#onCopyResponseClick.bind(this),
634
- onTextInputChange: this.#handleTextInputChange.bind(this),
635
- onLoadImage: isAiAssistanceMultimodalUploadInputEnabled() ? this.#handleLoadImage.bind(this) : undefined,
636
617
  }
637
618
  };
638
619
  }
@@ -806,10 +787,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
806
787
  SDK.TargetManager.TargetManager.instance().addModelListener(
807
788
  SDK.DOMModel.DOMModel, SDK.DOMModel.Events.AttrRemoved, this.#handleDOMNodeAttrChange, this);
808
789
 
809
- SDK.TargetManager.TargetManager.instance().addModelListener(
810
- SDK.ResourceTreeModel.ResourceTreeModel, SDK.ResourceTreeModel.Events.PrimaryPageChanged,
811
- this.#onPrimaryPageChanged, this);
812
-
813
790
  // Listen to changes in the Timeline Panel state. We also call the
814
791
  // function immediately in case the Performance panel is already shown
815
792
  // when AI Assistance is loaded.
@@ -863,9 +840,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
863
840
  this.#handleDOMNodeAttrChange,
864
841
  this,
865
842
  );
866
- SDK.TargetManager.TargetManager.instance().removeModelListener(
867
- SDK.ResourceTreeModel.ResourceTreeModel, SDK.ResourceTreeModel.Events.PrimaryPageChanged,
868
- this.#onPrimaryPageChanged, this);
869
843
 
870
844
  if (this.#timelinePanelInstance) {
871
845
  this.#timelinePanelInstance.removeEventListener(
@@ -944,15 +918,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
944
918
  this.#updateConversationState(this.#conversation);
945
919
  };
946
920
 
947
- #onPrimaryPageChanged(): void {
948
- if (!this.#imageInput) {
949
- return;
950
- }
951
-
952
- this.#imageInput = undefined;
953
- this.requestUpdate();
954
- }
955
-
956
921
  #getChangeSummary(): string|undefined {
957
922
  if (!isAiAssistancePatchingEnabled() || !this.#conversation || this.#conversation?.isReadOnly) {
958
923
  return;
@@ -1224,8 +1189,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1224
1189
  return;
1225
1190
  }
1226
1191
 
1227
- this.#imageInput = undefined;
1228
- this.#isTextInputEmpty = true;
1229
1192
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceQuerySubmitted);
1230
1193
  if (this.#conversation && this.#conversation.isBlockedByOrigin) {
1231
1194
  this.#handleNewChatRequest();
@@ -1322,100 +1285,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1322
1285
  }
1323
1286
  }
1324
1287
 
1325
- async #handleTakeScreenshot(): Promise<void> {
1326
- const mainTarget = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
1327
- if (!mainTarget) {
1328
- throw new Error('Could not find main target');
1329
- }
1330
- const model = mainTarget.model(SDK.ScreenCaptureModel.ScreenCaptureModel);
1331
- if (!model) {
1332
- throw new Error('Could not find model');
1333
- }
1334
- const showLoadingTimeout = setTimeout(() => {
1335
- this.#imageInput = {isLoading: true};
1336
- this.requestUpdate();
1337
- }, SHOW_LOADING_STATE_TIMEOUT);
1338
- const bytes = await model.captureScreenshot(
1339
- Protocol.Page.CaptureScreenshotRequestFormat.Jpeg,
1340
- SCREENSHOT_QUALITY,
1341
- SDK.ScreenCaptureModel.ScreenshotMode.FROM_VIEWPORT,
1342
- );
1343
- clearTimeout(showLoadingTimeout);
1344
- if (bytes) {
1345
- this.#imageInput = {
1346
- isLoading: false,
1347
- data: bytes,
1348
- mimeType: JPEG_MIME_TYPE,
1349
- inputType: AiAssistanceModel.AiAgent.MultimodalInputType.SCREENSHOT
1350
- };
1351
- this.requestUpdate();
1352
- void this.updateComplete.then(() => {
1353
- this.#viewOutput.chatView?.focusTextInput();
1354
- });
1355
- } else {
1356
- this.#imageInput = undefined;
1357
- this.requestUpdate();
1358
- Snackbars.Snackbar.Snackbar.show({
1359
- message: lockedString(UIStringsNotTranslate.screenshotFailureMessage),
1360
- });
1361
- }
1362
- }
1363
-
1364
- #handleRemoveImageInput(): void {
1365
- this.#imageInput = undefined;
1366
- this.requestUpdate();
1367
- void this.updateComplete.then(() => {
1368
- this.#viewOutput.chatView?.focusTextInput();
1369
- });
1370
- }
1371
-
1372
- #handleTextInputChange(value: string): void {
1373
- const disableSubmit = !value;
1374
- if (disableSubmit !== this.#isTextInputEmpty) {
1375
- this.#isTextInputEmpty = disableSubmit;
1376
- void this.requestUpdate();
1377
- }
1378
- }
1379
-
1380
- async #handleLoadImage(file: File): Promise<void> {
1381
- const showLoadingTimeout = setTimeout(() => {
1382
- this.#imageInput = {isLoading: true};
1383
- this.requestUpdate();
1384
- }, SHOW_LOADING_STATE_TIMEOUT);
1385
- try {
1386
- const reader = new FileReader();
1387
- const dataUrl = await new Promise<string>((resolve, reject) => {
1388
- reader.onload = () => {
1389
- if (typeof reader.result === 'string') {
1390
- resolve(reader.result);
1391
- } else {
1392
- reject(new Error('FileReader result was not a string.'));
1393
- }
1394
- };
1395
- reader.readAsDataURL(file);
1396
- });
1397
- const commaIndex = dataUrl.indexOf(',');
1398
- const bytes = dataUrl.substring(commaIndex + 1);
1399
- this.#imageInput = {
1400
- isLoading: false,
1401
- data: bytes,
1402
- mimeType: file.type,
1403
- inputType: AiAssistanceModel.AiAgent.MultimodalInputType.UPLOADED_IMAGE
1404
- };
1405
- } catch {
1406
- this.#imageInput = undefined;
1407
- Snackbars.Snackbar.Snackbar.show({
1408
- message: lockedString(UIStringsNotTranslate.uploadImageFailureMessage),
1409
- });
1410
- }
1411
-
1412
- clearTimeout(showLoadingTimeout);
1413
- this.requestUpdate();
1414
- void this.updateComplete.then(() => {
1415
- this.#viewOutput.chatView?.focusTextInput();
1416
- });
1417
- }
1418
-
1419
1288
  #cancel(): void {
1420
1289
  this.#runAbortController.abort();
1421
1290
  this.#runAbortController = new AbortController();
@@ -12,6 +12,7 @@ import * as i18n from '../../core/i18n/i18n.js';
12
12
  import * as Platform from '../../core/platform/platform.js';
13
13
  import * as Root from '../../core/root/root.js';
14
14
  import * as AiAssistanceModel from '../../models/ai_assistance/ai_assistance.js';
15
+ import * as GreenDev from '../../models/greendev/greendev.js';
15
16
  import * as Persistence from '../../models/persistence/persistence.js';
16
17
  import * as Workspace from '../../models/workspace/workspace.js';
17
18
  import * as WorkspaceDiff from '../../models/workspace_diff/workspace_diff.js';
@@ -186,41 +187,42 @@ export interface ViewOutput {
186
187
  }
187
188
 
188
189
  type View = (input: ViewInput, output: ViewOutput, target: HTMLElement) => void;
189
- const DEFAULT_VIEW: View =
190
- (input, output, target) => {
191
- if (!input.changeSummary && input.patchSuggestionState === PatchSuggestionState.INITIAL) {
192
- return;
193
- }
190
+ const DEFAULT_VIEW: View = (input, output, target) => {
191
+ if (!input.changeSummary && input.patchSuggestionState === PatchSuggestionState.INITIAL) {
192
+ return;
193
+ }
194
194
 
195
- output.changeRef = output.changeRef ?? Directives.createRef<HTMLElement>();
196
- output.summaryRef = output.summaryRef ?? Directives.createRef<HTMLElement>();
195
+ output.changeRef = output.changeRef ?? Directives.createRef<HTMLElement>();
196
+ output.summaryRef = output.summaryRef ?? Directives.createRef<HTMLElement>();
197
197
 
198
- function renderSourcesLink(): LitTemplate {
199
- if (!input.sources) {
200
- return nothing;
201
- }
198
+ function renderSourcesLink(): LitTemplate {
199
+ if (!input.sources) {
200
+ return nothing;
201
+ }
202
202
 
203
- return html`<x-link
203
+ return html`<x-link
204
204
  class="link"
205
205
  title="${UIStringsNotTranslate.viewUploadedFiles} ${UIStringsNotTranslate.opensInNewTab}"
206
206
  href="data:text/plain;charset=utf-8,${encodeURIComponent(input.sources)}"
207
- jslog=${VisualLogging.link('files-used-in-patching').track({click: true})}>
207
+ jslog=${VisualLogging.link('files-used-in-patching').track({
208
+ click: true
209
+ })}>
208
210
  ${UIStringsNotTranslate.viewUploadedFiles}
209
211
  </x-link>`;
210
- }
212
+ }
211
213
 
212
- function renderHeader(): LitTemplate {
213
- if (input.savedToDisk) {
214
- return html`
214
+ function renderHeader(): LitTemplate {
215
+ if (input.savedToDisk) {
216
+ return html`
215
217
  <devtools-icon class="green-bright-icon summary-badge" name="check-circle"></devtools-icon>
216
218
  <span class="header-text">
217
219
  ${lockedString(UIStringsNotTranslate.savedToDisk)}
218
220
  </span>
219
221
  `;
220
- }
222
+ }
221
223
 
222
- if (input.patchSuggestionState === PatchSuggestionState.SUCCESS) {
223
- return html`
224
+ if (input.patchSuggestionState === PatchSuggestionState.SUCCESS) {
225
+ return html`
224
226
  <devtools-icon class="on-tonal-icon summary-badge" name="difference"></devtools-icon>
225
227
  <span class="header-text">
226
228
  ${lockedString(`File changes in ${input.projectName}`)}
@@ -230,9 +232,9 @@ const DEFAULT_VIEW: View =
230
232
  name="chevron-down"
231
233
  ></devtools-icon>
232
234
  `;
233
- }
235
+ }
234
236
 
235
- return html`
237
+ return html`
236
238
  <devtools-icon class="on-tonal-icon summary-badge" name="pen-spark"></devtools-icon>
237
239
  <span class="header-text">
238
240
  ${lockedString(UIStringsNotTranslate.unsavedChanges)}
@@ -242,49 +244,63 @@ const DEFAULT_VIEW: View =
242
244
  name="chevron-down"
243
245
  ></devtools-icon>
244
246
  `;
245
- }
247
+ }
246
248
 
247
- function renderContent(): LitTemplate {
248
- if ((!input.changeSummary && input.patchSuggestionState === PatchSuggestionState.INITIAL) || input.savedToDisk) {
249
- return nothing;
250
- }
249
+ function renderContent(): LitTemplate {
250
+ if ((!input.changeSummary && input.patchSuggestionState === PatchSuggestionState.INITIAL) || input.savedToDisk) {
251
+ return nothing;
252
+ }
251
253
 
252
- if (input.patchSuggestionState === PatchSuggestionState.SUCCESS) {
253
- return html`<devtools-widget .widgetConfig=${UI.Widget.widgetConfig(ChangesPanel.CombinedDiffView.CombinedDiffView, {
254
+ if (input.patchSuggestionState === PatchSuggestionState.SUCCESS) {
255
+ return html`<devtools-widget .widgetConfig=${
256
+ UI.Widget.widgetConfig(ChangesPanel.CombinedDiffView.CombinedDiffView, {
254
257
  workspaceDiff: input.workspaceDiff,
255
258
  // Ignore user creates inspector-stylesheets
256
259
  ignoredUrls: ['inspector://']
257
260
  })}></devtools-widget>`;
258
- }
261
+ }
259
262
 
260
- return html`<devtools-code-block
263
+ return html`<devtools-code-block
261
264
  .code=${input.changeSummary ?? ''}
262
265
  .codeLang=${'css'}
263
266
  .displayNotice=${true}
264
267
  ></devtools-code-block>
265
- ${input.patchSuggestionState === PatchSuggestionState.ERROR
266
- ? html`<div class="error-container">
268
+ ${
269
+ input.patchSuggestionState === PatchSuggestionState.ERROR ?
270
+ html`<div class="error-container">
267
271
  <devtools-icon name="cross-circle-filled"></devtools-icon>${
268
- lockedString(UIStringsNotTranslate.genericErrorMessage)
269
- } ${renderSourcesLink()}
270
- </div>`
271
- : nothing
272
- }`;
273
- }
272
+ lockedString(UIStringsNotTranslate.genericErrorMessage)} ${renderSourcesLink()}
273
+ </div>` :
274
+ nothing}`;
275
+ }
274
276
 
275
- function renderFooter(): LitTemplate {
276
- if (input.savedToDisk) {
277
- return nothing;
278
- }
277
+ function renderCopyPrompt(changedCode?: string): LitTemplate {
278
+ if (!GreenDev.Prototypes.instance().isEnabled('copyToGemini') || !changedCode) {
279
+ return nothing;
280
+ }
281
+
282
+ // clang-format off
283
+ return html`<devtools-widget class="copy-to-prompt"
284
+ .widgetConfig=${UI.Widget.widgetConfig(PanelCommon.CopyChangesToPrompt, {
285
+ workspaceDiff: input.workspaceDiff,
286
+ patchAgentCSSChange: changedCode,
287
+ })}></devtools-widget>`;
288
+ // clang-format on
289
+ }
279
290
 
280
- if (input.patchSuggestionState === PatchSuggestionState.SUCCESS) {
281
- return html`
291
+ function renderFooter(): LitTemplate {
292
+ if (input.savedToDisk) {
293
+ return nothing;
294
+ }
295
+
296
+ if (input.patchSuggestionState === PatchSuggestionState.SUCCESS) {
297
+ return html`
282
298
  <div class="footer">
283
299
  <div class="left-side">
284
300
  <x-link class="link disclaimer-link" href="https://support.google.com/legal/answer/13505487" jslog=${
285
- VisualLogging.link('code-disclaimer').track({
286
- click: true,
287
- })}>
301
+ VisualLogging.link('code-disclaimer').track({
302
+ click: true,
303
+ })}>
288
304
  ${lockedString(UIStringsNotTranslate.codeDisclaimer)}
289
305
  </x-link>
290
306
  ${renderSourcesLink()}
@@ -305,18 +321,20 @@ const DEFAULT_VIEW: View =
305
321
  </div>
306
322
  </div>
307
323
  `;
308
- }
324
+ }
309
325
 
310
- const iconName = input.projectType === SelectedProjectType.AUTOMATIC_DISCONNECTED ? 'folder-off' : input.projectType === SelectedProjectType.AUTOMATIC_CONNECTED ? 'folder-asterisk' : 'folder';
311
- return html`
326
+ const iconName = input.projectType === SelectedProjectType.AUTOMATIC_DISCONNECTED ? 'folder-off' :
327
+ input.projectType === SelectedProjectType.AUTOMATIC_CONNECTED ? 'folder-asterisk' :
328
+ 'folder';
329
+ return html`
312
330
  <div class="footer">
313
331
  ${
314
- input.projectName ? html`
332
+ input.projectName ? html`
315
333
  <div class="change-workspace" jslog=${VisualLogging.section('patch-widget.workspace')}>
316
334
  <devtools-icon .name=${iconName}></devtools-icon>
317
335
  <span class="folder-name" title=${input.projectPath}>${input.projectName}</span>
318
336
  ${
319
- input.onChangeWorkspaceClick ? html`
337
+ input.onChangeWorkspaceClick ? html`
320
338
  <devtools-button
321
339
  @click=${input.onChangeWorkspaceClick}
322
340
  .jslogContext=${'change-workspace'}
@@ -326,23 +344,24 @@ const DEFAULT_VIEW: View =
326
344
  ${Directives.ref(output.changeRef)}
327
345
  >${lockedString(UIStringsNotTranslate.change)}</devtools-button>
328
346
  ` :
329
- nothing}
347
+ nothing}
330
348
  </div>
331
349
  ` :
332
- nothing}
350
+ nothing}
333
351
  <div class="apply-to-workspace-container" aria-live="polite">
334
352
  ${
335
- input.patchSuggestionState === PatchSuggestionState.LOADING ?
336
- html`
353
+ input.patchSuggestionState === PatchSuggestionState.LOADING ?
354
+ html`
337
355
  <div class="loading-text-container" jslog=${
338
- VisualLogging.section('patch-widget.apply-to-workspace-loading')}>
356
+ VisualLogging.section('patch-widget.apply-to-workspace-loading')}>
339
357
  <devtools-spinner></devtools-spinner>
340
358
  <span>
341
359
  ${lockedString(UIStringsNotTranslate.applyingToWorkspace)}
342
360
  </span>
343
361
  </div>
344
362
  ` :
345
- html`
363
+ html`
364
+ ${renderCopyPrompt(input.changeSummary)}
346
365
  <devtools-button
347
366
  @click=${input.onApplyToWorkspace}
348
367
  .jslogContext=${'patch-widget.apply-to-workspace'}
@@ -351,13 +370,13 @@ const DEFAULT_VIEW: View =
351
370
  </devtools-button>
352
371
  `}
353
372
  ${
354
- input.patchSuggestionState === PatchSuggestionState.LOADING ? html`<devtools-button
373
+ input.patchSuggestionState === PatchSuggestionState.LOADING ? html`<devtools-button
355
374
  @click=${input.onCancel}
356
375
  .jslogContext=${'cancel'}
357
376
  .variant=${Buttons.Button.Variant.OUTLINED}>
358
377
  ${lockedString(UIStringsNotTranslate.cancel)}
359
378
  </devtools-button>` :
360
- nothing}
379
+ nothing}
361
380
  <devtools-button
362
381
  aria-details="info-tooltip"
363
382
  .jslogContext=${'patch-widget.info-tooltip-trigger'}
@@ -374,26 +393,25 @@ const DEFAULT_VIEW: View =
374
393
  class="link tooltip-link"
375
394
  role="link"
376
395
  jslog=${VisualLogging.link('open-ai-settings').track({
377
- click: true,
378
- })}
396
+ click: true,
397
+ })}
379
398
  @click=${input.onLearnMoreTooltipClick}
380
399
  >${lockedString(UIStringsNotTranslate.learnMore)}</button>
381
400
  </div>
382
401
  </devtools-tooltip>
383
402
  </div>
384
403
  </div>`;
385
- }
404
+ }
386
405
 
387
- // Use a simple div for the "Saved to disk" state as it's not expandable,
388
- // otherwise use the interactive <details> element.
389
- const template = input.savedToDisk
390
- ? html`
406
+ // Use a simple div for the "Saved to disk" state as it's not expandable,
407
+ // otherwise use the interactive <details> element.
408
+ const template = input.savedToDisk ? html`
391
409
  <div class="change-summary saved-to-disk" role="status" aria-live="polite">
392
410
  <div class="header-container">
393
411
  ${renderHeader()}
394
412
  </div>
395
- </div>`
396
- : html`
413
+ </div>` :
414
+ html`
397
415
  <details class="change-summary" jslog=${VisualLogging.section('patch-widget')}>
398
416
  <summary class="header-container" ${Directives.ref(output.summaryRef)}>
399
417
  ${renderHeader()}
@@ -403,8 +421,8 @@ const DEFAULT_VIEW: View =
403
421
  </details>
404
422
  `;
405
423
 
406
- render(template, target);
407
- };
424
+ render(template, target);
425
+ };
408
426
 
409
427
  export class PatchWidget extends UI.Widget.Widget {
410
428
  changeSummary = '';
@@ -4,6 +4,7 @@
4
4
 
5
5
  export * from './AiAssistancePanel.js';
6
6
  export * from './components/ChatView.js';
7
+ export * as ChatInput from './components/ChatInput.js';
7
8
  export * from './components/MarkdownRendererWithCodeBlock.js';
8
9
  export * from './components/PerformanceAgentFlameChart.js';
9
10
  export * from './SelectWorkspaceDialog.js';