chrome-devtools-frontend 1.0.1611825 → 1.0.1613465

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 (39) hide show
  1. package/front_end/Images/src/flowsheet.svg +1 -0
  2. package/front_end/core/common/Color.ts +3 -2
  3. package/front_end/core/platform/StringUtilities.ts +5 -0
  4. package/front_end/core/sdk/NetworkManager.ts +7 -0
  5. package/front_end/generated/InspectorBackendCommands.ts +2 -2
  6. package/front_end/generated/protocol.ts +12 -2
  7. package/front_end/models/greendev/Prototypes.ts +7 -4
  8. package/front_end/models/har/HARFormat.ts +30 -0
  9. package/front_end/models/har/Importer.ts +12 -1
  10. package/front_end/models/har/Log.ts +28 -0
  11. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +62 -22
  12. package/front_end/panels/ai_assistance/PatchWidget.ts +7 -23
  13. package/front_end/panels/ai_assistance/SelectWorkspaceDialog.ts +3 -3
  14. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +7 -0
  15. package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
  16. package/front_end/panels/ai_assistance/components/ChatView.ts +4 -0
  17. package/front_end/panels/ai_assistance/components/ExportForAgentsDialog.ts +12 -6
  18. package/front_end/panels/ai_assistance/components/OptInChangeDialog.ts +179 -0
  19. package/front_end/panels/ai_assistance/components/optInChangeDialog.css +95 -0
  20. package/front_end/panels/ai_assistance/components/walkthroughView.css +1 -1
  21. package/front_end/panels/application/CookieItemsView.ts +3 -2
  22. package/front_end/panels/changes/ChangesView.ts +0 -12
  23. package/front_end/panels/common/common.ts +0 -1
  24. package/front_end/panels/console/ConsolePrompt.ts +2 -2
  25. package/front_end/panels/elements/StylePropertyTreeElement.ts +2 -1
  26. package/front_end/panels/elements/StylesSidebarPane.ts +3 -1
  27. package/front_end/panels/elements/elements-meta.ts +14 -0
  28. package/front_end/panels/network/NetworkLogView.ts +58 -28
  29. package/front_end/panels/recorder/RecorderController.ts +4 -4
  30. package/front_end/panels/settings/AISettingsTab.ts +39 -3
  31. package/front_end/panels/settings/SettingsScreen.ts +2 -2
  32. package/front_end/third_party/chromium/README.chromium +1 -1
  33. package/front_end/ui/legacy/SplitWidget.ts +9 -6
  34. package/front_end/ui/legacy/components/settings_ui/SettingsUI.ts +3 -3
  35. package/front_end/ui/legacy/components/source_frame/FontView.ts +101 -88
  36. package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +4 -4
  37. package/front_end/ui/visual_logging/KnownContextValues.ts +6 -0
  38. package/package.json +1 -1
  39. package/front_end/panels/common/CopyChangesToPrompt.ts +0 -233
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" height="20px" viewBox="0 -960 960 960" width="20px" fill="currentColor"><path d="M168-456v192-432V-456Zm383.79 312q15.21 0 25.71-10.29t10.5-25.5q0-15.21-10.29-25.71t-25.5-10.5q-15.21 0-25.71 10.29t-10.5 25.5q0 15.21 10.29 25.71t25.5 10.5Zm240-360q15.21 0 25.71-10.29t10.5-25.5q0-15.21-10.29-25.71t-25.5-10.5q-15.21 0-25.71 10.29t-10.5 25.5q0 15.21 10.29 25.71t25.5 10.5ZM240-528h204v-72H240v72Zm0 168h204v-72H240v72Zm-72 168q-29.7 0-50.85-21.16Q96-234.32 96-264.04v-432.24Q96-726 117.15-747T168-768h624q29.7 0 50.85 21.15Q864-725.7 864-696H168v432h204v72H168Zm307.5 88.52Q444-134.96 444-179.92 444-215 464-243t52-39v-102h240v-54q-32-11-52-38.92t-20-63.05q0-45.03 31.5-76.53T792-648q45 0 76.5 31.48t31.5 76.44Q900-505 880-477t-52 39v126H588v30q32 11 52 38.92t20 63.05q0 45.03-31.5 76.53T552-72q-45 0-76.5-31.48Z"/></svg>
@@ -684,6 +684,7 @@ export interface Color {
684
684
 
685
685
  const EPSILON = 0.01;
686
686
  const WIDE_RANGE_EPSILON = 1; // For comparisons on channels with a wider range than [0,1]
687
+ const STRICT_EPSILON = 1e-4;
687
688
  function equals(a: number[], b: number[], accuracy?: number): boolean;
688
689
  function equals(a: number|null, b: number|null, accuracy?: number): boolean;
689
690
  function equals(a: number|null|number[], b: number|null|number[], accuracy = EPSILON): boolean {
@@ -994,7 +995,7 @@ export class LCH implements Color {
994
995
  // See "powerless" component definitions in
995
996
  // https://www.w3.org/TR/css-color-4/#specifying-lab-lch
996
997
  isHuePowerless(): boolean {
997
- return equals(this.c, 0);
998
+ return equals(this.c, 0, STRICT_EPSILON);
998
999
  }
999
1000
  static fromSpec(spec: ColorParameterSpec, text: string): LCH|null {
1000
1001
  const L = parsePercentage(spec[0], [0, 100]) ?? parseNumber(spec[0]);
@@ -1592,7 +1593,7 @@ export class HSL implements Color {
1592
1593
  this.l = clamp(l, {min: 0, max: 1});
1593
1594
  s = equals(this.l, 0) || equals(this.l, 1) ? 0 : s;
1594
1595
  this.s = clamp(s, {min: 0, max: 1});
1595
- h = equals(this.s, 0) ? 0 : h;
1596
+ h = equals(this.s, 0, STRICT_EPSILON) ? 0 : h;
1596
1597
  this.h = normalizeHue(h * 360) / 360;
1597
1598
  this.alpha = clamp(alpha ?? null, {min: 0, max: 1});
1598
1599
  this.#authoredText = authoredText;
@@ -276,6 +276,7 @@ export const removeURLFragment = (inputStr: string): string => {
276
276
  };
277
277
 
278
278
  const SPECIAL_REGEX_CHARACTERS = '^[]{}()\\.^$*+?|-,';
279
+ const SPECIAL_URL_PATTERN_CHARACTERS = '?+*(){}\\:';
279
280
 
280
281
  export const regexSpecialCharacters = function(): string {
281
282
  return SPECIAL_REGEX_CHARACTERS;
@@ -450,6 +451,10 @@ export const escapeForRegExp = (str: string): string => {
450
451
  return escapeCharacters(str, SPECIAL_REGEX_CHARACTERS);
451
452
  };
452
453
 
454
+ export const escapeForURLPattern = (text: string): string => {
455
+ return escapeCharacters(text, SPECIAL_URL_PATTERN_CHARACTERS);
456
+ };
457
+
453
458
  export const naturalOrderComparator = (a: string, b: string): number => {
454
459
  const chunk = /^\d+|^\D+/;
455
460
  let chunkA, chunkB, numA, numB;
@@ -32,6 +32,10 @@ const UIStrings = {
32
32
  * @description Explanation why no content is shown for WebSocket connection.
33
33
  */
34
34
  noContentForWebSocket: 'Content for WebSockets is currently not supported',
35
+ /**
36
+ * @description Explanation why no content is shown for Server-Sent Events (SSE).
37
+ */
38
+ noContentForSSE: 'Content for Server-Sent Events (SSE) is currently not supported',
35
39
  /**
36
40
  * @description Explanation why no content is shown for redirect response.
37
41
  */
@@ -215,6 +219,9 @@ export class NetworkManager extends SDKModel<EventTypes> {
215
219
  return {error: i18nString(UIStrings.noContentForWebSocket)};
216
220
  }
217
221
  if (!request.finished) {
222
+ if (Boolean(request.eventSourceMessages()?.length)) {
223
+ return {error: i18nString(UIStrings.noContentForSSE)};
224
+ }
218
225
  await request.once(NetworkRequestEvents.FINISHED_LOADING);
219
226
  }
220
227
  if (request.isRedirect()) {
@@ -867,7 +867,7 @@ inspectorBackend.registerCommand("Network.continueInterceptedRequest", [{"name":
867
867
  inspectorBackend.registerCommand("Network.deleteCookies", [{"name": "name", "type": "string", "optional": false, "description": "Name of the cookies to remove.", "typeRef": null}, {"name": "url", "type": "string", "optional": true, "description": "If specified, deletes all the cookies with the given name where domain and path match provided URL.", "typeRef": null}, {"name": "domain", "type": "string", "optional": true, "description": "If specified, deletes only cookies with the exact domain.", "typeRef": null}, {"name": "path", "type": "string", "optional": true, "description": "If specified, deletes only cookies with the exact path.", "typeRef": null}, {"name": "partitionKey", "type": "object", "optional": true, "description": "If specified, deletes only cookies with the the given name and partitionKey where all partition key attributes match the cookie partition key attribute.", "typeRef": "Network.CookiePartitionKey"}], [], "Deletes browser cookies with matching name and url or domain/path/partitionKey pair.");
868
868
  inspectorBackend.registerCommand("Network.disable", [], [], "Disables network tracking, prevents network events from being sent to the client.");
869
869
  inspectorBackend.registerCommand("Network.emulateNetworkConditions", [{"name": "offline", "type": "boolean", "optional": false, "description": "True to emulate internet disconnection.", "typeRef": null}, {"name": "latency", "type": "number", "optional": false, "description": "Minimum latency from request sent to response headers received (ms).", "typeRef": null}, {"name": "downloadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated download throughput (bytes/sec). -1 disables download throttling.", "typeRef": null}, {"name": "uploadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated upload throughput (bytes/sec). -1 disables upload throttling.", "typeRef": null}, {"name": "connectionType", "type": "string", "optional": true, "description": "Connection type if known.", "typeRef": "Network.ConnectionType"}, {"name": "packetLoss", "type": "number", "optional": true, "description": "WebRTC packet loss (percent, 0-100). 0 disables packet loss emulation, 100 drops all the packets.", "typeRef": null}, {"name": "packetQueueLength", "type": "number", "optional": true, "description": "WebRTC packet queue length (packet). 0 removes any queue length limitations.", "typeRef": null}, {"name": "packetReordering", "type": "boolean", "optional": true, "description": "WebRTC packetReordering feature.", "typeRef": null}], [], "Activates emulation of network conditions. This command is deprecated in favor of the emulateNetworkConditionsByRule and overrideNetworkState commands, which can be used together to the same effect.");
870
- inspectorBackend.registerCommand("Network.emulateNetworkConditionsByRule", [{"name": "offline", "type": "boolean", "optional": false, "description": "True to emulate internet disconnection.", "typeRef": null}, {"name": "matchedNetworkConditions", "type": "array", "optional": false, "description": "Configure conditions for matching requests. If multiple entries match a request, the first entry wins. Global conditions can be configured by leaving the urlPattern for the conditions empty. These global conditions are also applied for throttling of p2p connections.", "typeRef": "Network.NetworkConditions"}], ["ruleIds"], "Activates emulation of network conditions for individual requests using URL match patterns. Unlike the deprecated Network.emulateNetworkConditions this method does not affect `navigator` state. Use Network.overrideNetworkState to explicitly modify `navigator` behavior.");
870
+ inspectorBackend.registerCommand("Network.emulateNetworkConditionsByRule", [{"name": "offline", "type": "boolean", "optional": true, "description": "True to emulate internet disconnection. Deprecated, use the offline property in matchedNetworkConditions or emulateOfflineServiceWorker instead.", "typeRef": null}, {"name": "emulateOfflineServiceWorker", "type": "boolean", "optional": true, "description": "True to emulate offline service worker.", "typeRef": null}, {"name": "matchedNetworkConditions", "type": "array", "optional": false, "description": "Configure conditions for matching requests. If multiple entries match a request, the first entry wins. Global conditions can be configured by leaving the urlPattern for the conditions empty. These global conditions are also applied for throttling of p2p connections.", "typeRef": "Network.NetworkConditions"}], ["ruleIds"], "Activates emulation of network conditions for individual requests using URL match patterns. Unlike the deprecated Network.emulateNetworkConditions this method does not affect `navigator` state. Use Network.overrideNetworkState to explicitly modify `navigator` behavior.");
871
871
  inspectorBackend.registerCommand("Network.overrideNetworkState", [{"name": "offline", "type": "boolean", "optional": false, "description": "True to emulate internet disconnection.", "typeRef": null}, {"name": "latency", "type": "number", "optional": false, "description": "Minimum latency from request sent to response headers received (ms).", "typeRef": null}, {"name": "downloadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated download throughput (bytes/sec). -1 disables download throttling.", "typeRef": null}, {"name": "uploadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated upload throughput (bytes/sec). -1 disables upload throttling.", "typeRef": null}, {"name": "connectionType", "type": "string", "optional": true, "description": "Connection type if known.", "typeRef": "Network.ConnectionType"}], [], "Override the state of navigator.onLine and navigator.connection.");
872
872
  inspectorBackend.registerCommand("Network.enable", [{"name": "maxTotalBufferSize", "type": "number", "optional": true, "description": "Buffer size in bytes to use when preserving network payloads (XHRs, etc). This is the maximum number of bytes that will be collected by this DevTools session.", "typeRef": null}, {"name": "maxResourceBufferSize", "type": "number", "optional": true, "description": "Per-resource buffer size in bytes to use when preserving network payloads (XHRs, etc).", "typeRef": null}, {"name": "maxPostDataSize", "type": "number", "optional": true, "description": "Longest post body size (in bytes) that would be included in requestWillBeSent notification", "typeRef": null}, {"name": "reportDirectSocketTraffic", "type": "boolean", "optional": true, "description": "Whether DirectSocket chunk send/receive events should be reported.", "typeRef": null}, {"name": "enableDurableMessages", "type": "boolean", "optional": true, "description": "Enable storing response bodies outside of renderer, so that these survive a cross-process navigation. Requires maxTotalBufferSize to be set. Currently defaults to false. This field is being deprecated in favor of the dedicated configureDurableMessages command, due to the possibility of deadlocks when awaiting Network.enable before issuing Runtime.runIfWaitingForDebugger.", "typeRef": null}], [], "Enables network tracking, network events will now be delivered to the client.");
873
873
  inspectorBackend.registerCommand("Network.configureDurableMessages", [{"name": "maxTotalBufferSize", "type": "number", "optional": true, "description": "Buffer size in bytes to use when preserving network payloads (XHRs, etc).", "typeRef": null}, {"name": "maxResourceBufferSize", "type": "number", "optional": true, "description": "Per-resource buffer size in bytes to use when preserving network payloads (XHRs, etc).", "typeRef": null}], [], "Configures storing response bodies outside of renderer, so that these survive a cross-process navigation. If maxTotalBufferSize is not set, durable messages are disabled.");
@@ -923,7 +923,7 @@ inspectorBackend.registerType("Network.SignedExchangeSignature", [{"name": "labe
923
923
  inspectorBackend.registerType("Network.SignedExchangeHeader", [{"name": "requestUrl", "type": "string", "optional": false, "description": "Signed exchange request URL.", "typeRef": null}, {"name": "responseCode", "type": "number", "optional": false, "description": "Signed exchange response code.", "typeRef": null}, {"name": "responseHeaders", "type": "object", "optional": false, "description": "Signed exchange response headers.", "typeRef": "Network.Headers"}, {"name": "signatures", "type": "array", "optional": false, "description": "Signed exchange response signature.", "typeRef": "Network.SignedExchangeSignature"}, {"name": "headerIntegrity", "type": "string", "optional": false, "description": "Signed exchange header integrity hash in the form of `sha256-<base64-hash-value>`.", "typeRef": null}]);
924
924
  inspectorBackend.registerType("Network.SignedExchangeError", [{"name": "message", "type": "string", "optional": false, "description": "Error message.", "typeRef": null}, {"name": "signatureIndex", "type": "number", "optional": true, "description": "The index of the signature which caused the error.", "typeRef": null}, {"name": "errorField", "type": "string", "optional": true, "description": "The field which caused the error.", "typeRef": "Network.SignedExchangeErrorField"}]);
925
925
  inspectorBackend.registerType("Network.SignedExchangeInfo", [{"name": "outerResponse", "type": "object", "optional": false, "description": "The outer response of signed HTTP exchange which was received from network.", "typeRef": "Network.Response"}, {"name": "hasExtraInfo", "type": "boolean", "optional": false, "description": "Whether network response for the signed exchange was accompanied by extra headers.", "typeRef": null}, {"name": "header", "type": "object", "optional": true, "description": "Information about the signed exchange header.", "typeRef": "Network.SignedExchangeHeader"}, {"name": "securityDetails", "type": "object", "optional": true, "description": "Security details for the signed exchange header.", "typeRef": "Network.SecurityDetails"}, {"name": "errors", "type": "array", "optional": true, "description": "Errors occurred while handling the signed exchange.", "typeRef": "Network.SignedExchangeError"}]);
926
- inspectorBackend.registerType("Network.NetworkConditions", [{"name": "urlPattern", "type": "string", "optional": false, "description": "Only matching requests will be affected by these conditions. Patterns use the URLPattern constructor string syntax (https://urlpattern.spec.whatwg.org/) and must be absolute. If the pattern is empty, all requests are matched (including p2p connections).", "typeRef": null}, {"name": "latency", "type": "number", "optional": false, "description": "Minimum latency from request sent to response headers received (ms).", "typeRef": null}, {"name": "downloadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated download throughput (bytes/sec). -1 disables download throttling.", "typeRef": null}, {"name": "uploadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated upload throughput (bytes/sec). -1 disables upload throttling.", "typeRef": null}, {"name": "connectionType", "type": "string", "optional": true, "description": "Connection type if known.", "typeRef": "Network.ConnectionType"}, {"name": "packetLoss", "type": "number", "optional": true, "description": "WebRTC packet loss (percent, 0-100). 0 disables packet loss emulation, 100 drops all the packets.", "typeRef": null}, {"name": "packetQueueLength", "type": "number", "optional": true, "description": "WebRTC packet queue length (packet). 0 removes any queue length limitations.", "typeRef": null}, {"name": "packetReordering", "type": "boolean", "optional": true, "description": "WebRTC packetReordering feature.", "typeRef": null}]);
926
+ inspectorBackend.registerType("Network.NetworkConditions", [{"name": "urlPattern", "type": "string", "optional": false, "description": "Only matching requests will be affected by these conditions. Patterns use the URLPattern constructor string syntax (https://urlpattern.spec.whatwg.org/) and must be absolute. If the pattern is empty, all requests are matched (including p2p connections).", "typeRef": null}, {"name": "latency", "type": "number", "optional": false, "description": "Minimum latency from request sent to response headers received (ms).", "typeRef": null}, {"name": "downloadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated download throughput (bytes/sec). -1 disables download throttling.", "typeRef": null}, {"name": "uploadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated upload throughput (bytes/sec). -1 disables upload throttling.", "typeRef": null}, {"name": "connectionType", "type": "string", "optional": true, "description": "Connection type if known.", "typeRef": "Network.ConnectionType"}, {"name": "packetLoss", "type": "number", "optional": true, "description": "WebRTC packet loss (percent, 0-100). 0 disables packet loss emulation, 100 drops all the packets.", "typeRef": null}, {"name": "packetQueueLength", "type": "number", "optional": true, "description": "WebRTC packet queue length (packet). 0 removes any queue length limitations.", "typeRef": null}, {"name": "packetReordering", "type": "boolean", "optional": true, "description": "WebRTC packetReordering feature.", "typeRef": null}, {"name": "offline", "type": "boolean", "optional": true, "description": "True to emulate internet disconnection.", "typeRef": null}]);
927
927
  inspectorBackend.registerType("Network.BlockPattern", [{"name": "urlPattern", "type": "string", "optional": false, "description": "URL pattern to match. Patterns use the URLPattern constructor string syntax (https://urlpattern.spec.whatwg.org/) and must be absolute. Example: `*://*:*/*.css`.", "typeRef": null}, {"name": "block", "type": "boolean", "optional": false, "description": "Whether or not to block the pattern. If false, a matching request will not be blocked even if it matches a later `BlockPattern`.", "typeRef": null}]);
928
928
  inspectorBackend.registerType("Network.DirectTCPSocketOptions", [{"name": "noDelay", "type": "boolean", "optional": false, "description": "TCP_NODELAY option", "typeRef": null}, {"name": "keepAliveDelay", "type": "number", "optional": true, "description": "Expected to be unsigned integer.", "typeRef": null}, {"name": "sendBufferSize", "type": "number", "optional": true, "description": "Expected to be unsigned integer.", "typeRef": null}, {"name": "receiveBufferSize", "type": "number", "optional": true, "description": "Expected to be unsigned integer.", "typeRef": null}, {"name": "dnsQueryType", "type": "string", "optional": true, "description": "", "typeRef": "Network.DirectSocketDnsQueryType"}]);
929
929
  inspectorBackend.registerType("Network.DirectUDPSocketOptions", [{"name": "remoteAddr", "type": "string", "optional": true, "description": "", "typeRef": null}, {"name": "remotePort", "type": "number", "optional": true, "description": "Unsigned int 16.", "typeRef": null}, {"name": "localAddr", "type": "string", "optional": true, "description": "", "typeRef": null}, {"name": "localPort", "type": "number", "optional": true, "description": "Unsigned int 16.", "typeRef": null}, {"name": "dnsQueryType", "type": "string", "optional": true, "description": "", "typeRef": "Network.DirectSocketDnsQueryType"}, {"name": "sendBufferSize", "type": "number", "optional": true, "description": "Expected to be unsigned integer.", "typeRef": null}, {"name": "receiveBufferSize", "type": "number", "optional": true, "description": "Expected to be unsigned integer.", "typeRef": null}, {"name": "multicastLoopback", "type": "boolean", "optional": true, "description": "", "typeRef": null}, {"name": "multicastTimeToLive", "type": "number", "optional": true, "description": "Unsigned int 8.", "typeRef": null}, {"name": "multicastAllowAddressSharing", "type": "boolean", "optional": true, "description": "", "typeRef": null}]);
@@ -11376,6 +11376,10 @@ export namespace Network {
11376
11376
  * WebRTC packetReordering feature.
11377
11377
  */
11378
11378
  packetReordering?: boolean;
11379
+ /**
11380
+ * True to emulate internet disconnection.
11381
+ */
11382
+ offline?: boolean;
11379
11383
  }
11380
11384
 
11381
11385
  export interface BlockPattern {
@@ -12154,9 +12158,15 @@ export namespace Network {
12154
12158
 
12155
12159
  export interface EmulateNetworkConditionsByRuleRequest {
12156
12160
  /**
12157
- * True to emulate internet disconnection.
12161
+ * True to emulate internet disconnection. Deprecated, use the offline property in matchedNetworkConditions
12162
+ * or emulateOfflineServiceWorker instead.
12163
+ * @deprecated
12158
12164
  */
12159
- offline: boolean;
12165
+ offline?: boolean;
12166
+ /**
12167
+ * True to emulate offline service worker.
12168
+ */
12169
+ emulateOfflineServiceWorker?: boolean;
12160
12170
  /**
12161
12171
  * Configure conditions for matching requests. If multiple entries match a request, the first entry wins. Global
12162
12172
  * conditions can be configured by leaving the urlPattern for the conditions empty. These global conditions are
@@ -9,7 +9,7 @@ let instance: Prototypes|null = null;
9
9
 
10
10
  export interface GreenDevSettings {
11
11
  aiAnnotations: Common.Settings.Setting<boolean>;
12
- copyToGemini: Common.Settings.Setting<boolean>;
12
+ beyondStyling: Common.Settings.Setting<boolean>;
13
13
  breakpointDebuggerAgent: Common.Settings.Setting<boolean>;
14
14
  emulationCapabilities: Common.Settings.Setting<boolean>;
15
15
  }
@@ -42,8 +42,11 @@ export class Prototypes {
42
42
  false,
43
43
  Common.Settings.SettingStorageType.LOCAL,
44
44
  );
45
- const copyToGemini =
46
- settings.createSetting('greendev-copy-to-gemini-enabled', false, Common.Settings.SettingStorageType.LOCAL);
45
+ const beyondStyling = settings.createSetting(
46
+ 'greendev-beyond-styling-enabled',
47
+ false,
48
+ Common.Settings.SettingStorageType.LOCAL,
49
+ );
47
50
  const breakpointDebuggerAgent = settings.createSetting(
48
51
  'greendev-breakpoint-debugger-agent-enabled',
49
52
  false,
@@ -55,6 +58,6 @@ export class Prototypes {
55
58
  Common.Settings.SettingStorageType.LOCAL,
56
59
  );
57
60
 
58
- return {aiAnnotations, copyToGemini, breakpointDebuggerAgent, emulationCapabilities};
61
+ return {aiAnnotations, beyondStyling, breakpointDebuggerAgent, emulationCapabilities};
59
62
  }
60
63
  }
@@ -171,6 +171,7 @@ export class HAREntry extends HARBase {
171
171
  this.custom.set('initiator', this.importInitiator(data['_initiator']));
172
172
  this.custom.set('priority', HARBase.optionalString(data['_priority']));
173
173
  this.custom.set('resourceType', HARBase.optionalString(data['_resourceType']));
174
+ this.custom.set('eventSourceMessages', this.#importEventSourceMessages(data['_eventSourceMessages']));
174
175
  this.custom.set('webSocketMessages', this.importWebSocketMessages(data['_webSocketMessages']));
175
176
  }
176
177
 
@@ -196,6 +197,21 @@ export class HAREntry extends HARBase {
196
197
  }
197
198
  return outputMessages;
198
199
  }
200
+
201
+ #importEventSourceMessages(inputMessages: any): HAREventSourceMessage[]|undefined {
202
+ if (!Array.isArray(inputMessages)) {
203
+ return;
204
+ }
205
+
206
+ const outputMessages = [];
207
+ for (const message of inputMessages) {
208
+ if (typeof message !== 'object') {
209
+ return;
210
+ }
211
+ outputMessages.push(new HAREventSourceMessage(message));
212
+ }
213
+ return outputMessages;
214
+ }
199
215
  }
200
216
 
201
217
  class HARRequest extends HARBase {
@@ -480,3 +496,17 @@ class HARWebSocketMessage extends HARBase {
480
496
  this.type = HARBase.optionalString(data['type']);
481
497
  }
482
498
  }
499
+
500
+ class HAREventSourceMessage extends HARBase {
501
+ time: number|undefined;
502
+ eventName: string|undefined;
503
+ eventId: string|undefined;
504
+ data: string|undefined;
505
+ constructor(data: any) {
506
+ super(data);
507
+ this.time = HARBase.optionalNumber(data['time']);
508
+ this.eventName = HARBase.optionalString(data['eventName']);
509
+ this.eventId = HARBase.optionalString(data['eventId']);
510
+ this.data = HARBase.optionalString(data['data']);
511
+ }
512
+ }
@@ -137,7 +137,18 @@ export class Importer {
137
137
  async () =>
138
138
  new TextUtils.ContentData.ContentData(contentText ?? '', isBase64, mimeType ?? '', charset ?? undefined));
139
139
 
140
- if (request.mimeType === Platform.MimeType.MimeType.EVENTSTREAM && contentText) {
140
+ const importedEventSourceMessages = entry.customAsArray('eventSourceMessages');
141
+
142
+ if (importedEventSourceMessages) {
143
+ for (const message of importedEventSourceMessages) {
144
+ if (message.time === undefined || message.eventName === undefined || message.eventId === undefined) {
145
+ continue;
146
+ }
147
+ // message.data may be undefined, if saved in a sanitized context
148
+
149
+ request.addEventSourceMessage(message.time, message.eventName, message.eventId, message.data);
150
+ }
151
+ } else if (request.mimeType === Platform.MimeType.MimeType.EVENTSTREAM && contentText) {
141
152
  const issueTime = entry.startedDateTime.getTime() / 1000;
142
153
  const onEvent = (eventName: string, data: string, eventId: string): void => {
143
154
  request.addEventSourceMessage(issueTime, eventName, eventId, data);
@@ -176,6 +176,26 @@ export class Entry {
176
176
  delete entry._webSocketMessages;
177
177
  }
178
178
 
179
+ const eventSourceMessages = harEntry.request.eventSourceMessages();
180
+ if (eventSourceMessages?.length) {
181
+ const messages = [];
182
+ for (const message of eventSourceMessages) {
183
+ const messageDTO: EventSourceMessageDTO = {
184
+ time: message.time,
185
+ eventName: message.eventName,
186
+ eventId: message.eventId,
187
+ };
188
+ if (!options.sanitize) {
189
+ // Omit the data when sanitizing, as it could contain sensitive information.
190
+ messageDTO.data = message.data;
191
+ }
192
+ messages.push(messageDTO);
193
+ }
194
+ entry._eventSourceMessages = messages;
195
+ } else {
196
+ delete entry._eventSourceMessages;
197
+ }
198
+
179
199
  return entry;
180
200
  }
181
201
 
@@ -490,6 +510,7 @@ export interface EntryDTO {
490
510
  _priority: Protocol.Network.ResourcePriority|null;
491
511
  _resourceType: string;
492
512
  _webSocketMessages?: Object[];
513
+ _eventSourceMessages?: EventSourceMessageDTO[];
493
514
  cache: Object;
494
515
  connection?: string;
495
516
  pageref?: string;
@@ -540,3 +561,10 @@ export interface LogDTO {
540
561
  pages: Page[];
541
562
  entries: EntryDTO[];
542
563
  }
564
+
565
+ export interface EventSourceMessageDTO {
566
+ time: number;
567
+ eventName: string;
568
+ eventId: string;
569
+ data?: string;
570
+ }
@@ -44,6 +44,7 @@ import {
44
44
  import {DisabledWidget} from './components/DisabledWidget.js';
45
45
  import {ExploreWidget} from './components/ExploreWidget.js';
46
46
  import {MarkdownRendererWithCodeBlock} from './components/MarkdownRendererWithCodeBlock.js';
47
+ import {OptInChangeDialog} from './components/OptInChangeDialog.js';
47
48
  import {PerformanceAgentMarkdownRenderer} from './components/PerformanceAgentMarkdownRenderer.js';
48
49
  import {StylingAgentMarkdownRenderer} from './components/StylingAgentMarkdownRenderer.js';
49
50
  import {
@@ -252,6 +253,16 @@ const UIStringsNotTranslate = {
252
253
  */
253
254
  inputDisclaimerForAccessibilityEnterpriseNoLogging:
254
255
  'Chat messages and the selected Lighthouse report 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.',
256
+ /**
257
+ * @description Disclaimer text right after the chat input when V2 is enabled.
258
+ */
259
+ inputDisclaimerV2:
260
+ 'Chat messages, data accessible for this site via DevTools panels and Web APIs, and items you select such as network requests, files, and performance traces are sent to Google and may be seen by human reviewers to improve this feature. This is an experimental AI feature and won’t always get it right.',
261
+ /**
262
+ * @description Disclaimer text right after the chat input when V2 is enabled and enterprise logging is off.
263
+ */
264
+ inputDisclaimerEnterpriseNoLoggingV2:
265
+ 'Chat messages, data accessible for this site via DevTools panels and Web APIs, and items you select such as network requests, files, and performance traces are sent to Google. The content submitted to and 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.',
255
266
  } as const;
256
267
 
257
268
  const str_ = i18n.i18n.registerUIStrings('panels/ai_assistance/AiAssistancePanel.ts', UIStrings);
@@ -779,11 +790,32 @@ export class AiAssistancePanel extends UI.Panel.Panel {
779
790
  return await this.#conversationSummaryAgent.summarizeConversation(markdown);
780
791
  },
781
792
  onTextSubmit: async (
782
-
783
793
  text: string, imageInput?: Host.AidaClient.Part,
784
794
  multimodalInputType?: AiAssistanceModel.AiAgent.MultimodalInputType) => {
785
- Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceQuerySubmitted);
786
- await this.#startConversation(text, imageInput, multimodalInputType);
795
+ const submit = (): void => {
796
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceQuerySubmitted);
797
+ void this.#startConversation(text, imageInput, multimodalInputType);
798
+ };
799
+
800
+ const isAIV2Enabled = Root.Runtime.hostConfig.devToolsAiAssistanceV2?.enabled;
801
+ const seenSetting =
802
+ Common.Settings.Settings.instance().moduleSetting('ai-assistance-v2-opt-in-change-dialog-seen');
803
+ if (isAIV2Enabled && !seenSetting.get()) {
804
+ OptInChangeDialog.show({
805
+ onGotIt: () => {
806
+ seenSetting.set(true);
807
+ submit();
808
+ },
809
+ onManageSettings: () => {
810
+ seenSetting.set(true);
811
+ this.#viewOutput.chatView?.setInputValue(text);
812
+ void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
813
+ },
814
+ });
815
+ return;
816
+ }
817
+
818
+ submit();
787
819
  },
788
820
  onInspectElementClick: this.#handleSelectElementClick.bind(this),
789
821
  onFeedbackSubmit: this.#handleFeedbackSubmit.bind(this),
@@ -1396,46 +1428,54 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1396
1428
  return i18nString(UIStrings.inputDisclaimerForEmptyState);
1397
1429
  }
1398
1430
 
1399
- const noLogging = Root.Runtime.hostConfig.aidaAvailability?.enterprisePolicyValue ===
1431
+ const loggingEnabled = Root.Runtime.hostConfig.aidaAvailability?.enterprisePolicyValue !==
1400
1432
  Root.Runtime.GenAiEnterprisePolicyValue.ALLOW_WITHOUT_LOGGING;
1433
+
1434
+ if (Root.Runtime.hostConfig.devToolsAiAssistanceV2?.enabled) {
1435
+ if (loggingEnabled) {
1436
+ return lockedString(UIStringsNotTranslate.inputDisclaimerV2);
1437
+ }
1438
+ return lockedString(UIStringsNotTranslate.inputDisclaimerEnterpriseNoLoggingV2);
1439
+ }
1440
+
1401
1441
  switch (this.#conversation.type) {
1402
1442
  case AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING:
1403
- if (noLogging) {
1404
- return lockedString(UIStringsNotTranslate.inputDisclaimerForStylingEnterpriseNoLogging);
1443
+ if (loggingEnabled) {
1444
+ return lockedString(UIStringsNotTranslate.inputDisclaimerForStyling);
1405
1445
  }
1406
- return lockedString(UIStringsNotTranslate.inputDisclaimerForStyling);
1446
+ return lockedString(UIStringsNotTranslate.inputDisclaimerForStylingEnterpriseNoLogging);
1407
1447
  case AiAssistanceModel.AiHistoryStorage.ConversationType.FILE:
1408
- if (noLogging) {
1409
- return lockedString(UIStringsNotTranslate.inputDisclaimerForFileEnterpriseNoLogging);
1448
+ if (loggingEnabled) {
1449
+ return lockedString(UIStringsNotTranslate.inputDisclaimerForFile);
1410
1450
  }
1411
- return lockedString(UIStringsNotTranslate.inputDisclaimerForFile);
1451
+ return lockedString(UIStringsNotTranslate.inputDisclaimerForFileEnterpriseNoLogging);
1412
1452
  case AiAssistanceModel.AiHistoryStorage.ConversationType.NETWORK:
1413
- if (noLogging) {
1414
- return lockedString(UIStringsNotTranslate.inputDisclaimerForNetworkEnterpriseNoLogging);
1453
+ if (loggingEnabled) {
1454
+ return lockedString(UIStringsNotTranslate.inputDisclaimerForNetwork);
1415
1455
  }
1416
- return lockedString(UIStringsNotTranslate.inputDisclaimerForNetwork);
1456
+ return lockedString(UIStringsNotTranslate.inputDisclaimerForNetworkEnterpriseNoLogging);
1417
1457
 
1418
1458
  // It is deliberate that both Performance agents use the same disclaimer
1419
1459
  // text and this has been approved by Privacy.
1420
1460
  case AiAssistanceModel.AiHistoryStorage.ConversationType.PERFORMANCE:
1421
- if (noLogging) {
1422
- return lockedString(UIStringsNotTranslate.inputDisclaimerForPerformanceEnterpriseNoLogging);
1461
+ if (loggingEnabled) {
1462
+ return lockedString(UIStringsNotTranslate.inputDisclaimerForPerformance);
1423
1463
  }
1424
- return lockedString(UIStringsNotTranslate.inputDisclaimerForPerformance);
1464
+ return lockedString(UIStringsNotTranslate.inputDisclaimerForPerformanceEnterpriseNoLogging);
1425
1465
 
1426
1466
  case AiAssistanceModel.AiHistoryStorage.ConversationType.ACCESSIBILITY:
1427
- if (noLogging) {
1428
- return lockedString(UIStringsNotTranslate.inputDisclaimerForAccessibilityEnterpriseNoLogging);
1467
+ if (loggingEnabled) {
1468
+ return lockedString(UIStringsNotTranslate.inputDisclaimerForAccessibility);
1429
1469
  }
1430
- return lockedString(UIStringsNotTranslate.inputDisclaimerForAccessibility);
1470
+ return lockedString(UIStringsNotTranslate.inputDisclaimerForAccessibilityEnterpriseNoLogging);
1431
1471
 
1432
1472
  case AiAssistanceModel.AiHistoryStorage.ConversationType.BREAKPOINT:
1433
1473
 
1434
1474
  case AiAssistanceModel.AiHistoryStorage.ConversationType.NONE:
1435
- if (noLogging) {
1436
- return lockedString(UIStringsNotTranslate.inputDisclaimerForNoContextEnterpriseNoLogging);
1475
+ if (loggingEnabled) {
1476
+ return lockedString(UIStringsNotTranslate.inputDisclaimerForNoContext);
1437
1477
  }
1438
- return lockedString(UIStringsNotTranslate.inputDisclaimerForNoContext);
1478
+ return lockedString(UIStringsNotTranslate.inputDisclaimerForNoContextEnterpriseNoLogging);
1439
1479
  }
1440
1480
  }
1441
1481
 
@@ -13,7 +13,6 @@ import * as i18n from '../../core/i18n/i18n.js';
13
13
  import * as Platform from '../../core/platform/platform.js';
14
14
  import * as Root from '../../core/root/root.js';
15
15
  import * as AiAssistanceModel from '../../models/ai_assistance/ai_assistance.js';
16
- import * as GreenDev from '../../models/greendev/greendev.js';
17
16
  import * as Persistence from '../../models/persistence/persistence.js';
18
17
  import * as Workspace from '../../models/workspace/workspace.js';
19
18
  import * as WorkspaceDiff from '../../models/workspace_diff/workspace_diff.js';
@@ -273,20 +272,6 @@ const DEFAULT_VIEW: View = (input, output, target) => {
273
272
  nothing}`;
274
273
  }
275
274
 
276
- function renderCopyPrompt(changedCode?: string): LitTemplate {
277
- if (!GreenDev.Prototypes.instance().isEnabled('copyToGemini') || !changedCode) {
278
- return nothing;
279
- }
280
-
281
- // clang-format off
282
- return html`<devtools-widget class="copy-to-prompt"
283
- ${widget(PanelCommon.CopyChangesToPrompt, {
284
- workspaceDiff: input.workspaceDiff,
285
- patchAgentCSSChange: changedCode,
286
- })}></devtools-widget>`;
287
- // clang-format on
288
- }
289
-
290
275
  function renderFooter(): LitTemplate {
291
276
  if (input.savedToDisk) {
292
277
  return nothing;
@@ -357,7 +342,6 @@ const DEFAULT_VIEW: View = (input, output, target) => {
357
342
  </div>
358
343
  ` :
359
344
  html`
360
- ${renderCopyPrompt(input.changeSummary)}
361
345
  <devtools-button
362
346
  @click=${input.onApplyToWorkspace}
363
347
  .jslogContext=${'patch-widget.apply-to-workspace'}
@@ -435,7 +419,7 @@ export class PatchWidget extends UI.Widget.Widget {
435
419
  #project?: Workspace.Workspace.Project;
436
420
  #patchSources?: string;
437
421
  #savedToDisk?: boolean;
438
- #noLogging: boolean; // Whether the enterprise setting is `ALLOW_WITHOUT_LOGGING` or not.
422
+ #loggingEnabled: boolean; // Whether the enterprise setting is `ALLOW_WITHOUT_LOGGING` or not.
439
423
  #patchSuggestionState = PatchSuggestionState.INITIAL;
440
424
  #workspaceDiff = WorkspaceDiff.WorkspaceDiff.workspaceDiff();
441
425
  #workspace = Workspace.Workspace.WorkspaceImpl.instance();
@@ -450,7 +434,7 @@ export class PatchWidget extends UI.Widget.Widget {
450
434
  }) {
451
435
  super(element);
452
436
  this.#aidaClient = opts?.aidaClient ?? new Host.AidaClient.AidaClient();
453
- this.#noLogging = Root.Runtime.hostConfig.aidaAvailability?.enterprisePolicyValue ===
437
+ this.#loggingEnabled = Root.Runtime.hostConfig.aidaAvailability?.enterprisePolicyValue !==
454
438
  Root.Runtime.GenAiEnterprisePolicyValue.ALLOW_WITHOUT_LOGGING;
455
439
  this.#view = view;
456
440
 
@@ -514,9 +498,9 @@ export class PatchWidget extends UI.Widget.Widget {
514
498
  projectPath,
515
499
  projectType: this.#getSelectedProjectType(projectPath),
516
500
  savedToDisk: this.#savedToDisk,
517
- applyToWorkspaceTooltipText: this.#noLogging ?
518
- lockedString(UIStringsNotTranslate.applyToWorkspaceTooltipNoLogging) :
519
- lockedString(UIStringsNotTranslate.applyToWorkspaceTooltip),
501
+ applyToWorkspaceTooltipText: this.#loggingEnabled ?
502
+ lockedString(UIStringsNotTranslate.applyToWorkspaceTooltip) :
503
+ lockedString(UIStringsNotTranslate.applyToWorkspaceTooltipNoLogging),
520
504
  onLearnMoreTooltipClick: this.#onLearnMoreTooltipClick.bind(this),
521
505
  onApplyToWorkspace: this.#onApplyToWorkspace.bind(this),
522
506
  onCancel: () => {
@@ -566,8 +550,8 @@ export class PatchWidget extends UI.Widget.Widget {
566
550
  },
567
551
  {
568
552
  iconName: 'google',
569
- content: this.#noLogging ? lockedString(UIStringsNotTranslate.freDisclaimerTextPrivacyNoLogging) :
570
- lockedString(UIStringsNotTranslate.freDisclaimerTextPrivacy),
553
+ content: this.#loggingEnabled ? lockedString(UIStringsNotTranslate.freDisclaimerTextPrivacy) :
554
+ lockedString(UIStringsNotTranslate.freDisclaimerTextPrivacyNoLogging),
571
555
  },
572
556
  {
573
557
  iconName: 'warning',
@@ -233,14 +233,14 @@ export class SelectWorkspaceDialog extends UI.Widget.VBox {
233
233
  }
234
234
 
235
235
  override performUpdate(): void {
236
- const noLogging = Root.Runtime.hostConfig.aidaAvailability?.enterprisePolicyValue ===
236
+ const loggingEnabled = Root.Runtime.hostConfig.aidaAvailability?.enterprisePolicyValue !==
237
237
  Root.Runtime.GenAiEnterprisePolicyValue.ALLOW_WITHOUT_LOGGING;
238
238
 
239
239
  const viewInput = {
240
240
  folders: this.#folders,
241
241
  selectedIndex: this.#selectedIndex,
242
- selectProjectRootText: noLogging ? lockedString(UIStringsNotTranslate.selectProjectRootNoLogging) :
243
- lockedString(UIStringsNotTranslate.selectProjectRoot),
242
+ selectProjectRootText: loggingEnabled ? lockedString(UIStringsNotTranslate.selectProjectRoot) :
243
+ lockedString(UIStringsNotTranslate.selectProjectRootNoLogging),
244
244
  showAutomaticWorkspaceNudge: this.#automaticFileSystemManager.automaticFileSystem === null &&
245
245
  this.#automaticFileSystemManager.availability === 'available',
246
246
  onProjectSelected: (index: number) => {
@@ -161,6 +161,13 @@ Common.Settings.registerSettingExtension({
161
161
  },
162
162
  });
163
163
 
164
+ Common.Settings.registerSettingExtension({
165
+ category: Common.Settings.SettingCategory.AI,
166
+ settingName: 'ai-assistance-v2-opt-in-change-dialog-seen',
167
+ settingType: Common.Settings.SettingType.BOOLEAN,
168
+ defaultValue: false,
169
+ });
170
+
164
171
  UI.ActionRegistration.registerActionExtension({
165
172
  actionId: 'freestyler.main-menu',
166
173
  contextTypes(): [] {
@@ -11,6 +11,7 @@ export * from './SelectWorkspaceDialog.js';
11
11
  export * as ChatMessage from './components/ChatMessage.js';
12
12
  export * as ExploreWidget from './components/ExploreWidget.js';
13
13
  export * as ExportForAgentsDialog from './components/ExportForAgentsDialog.js';
14
+ export * as OptInChangeDialog from './components/OptInChangeDialog.js';
14
15
  export * as PatchWidget from './PatchWidget.js';
15
16
  export * as DisabledWidget from './components/DisabledWidget.js';
16
17
  export * as WalkthroughView from './components/WalkthroughView.js';
@@ -282,6 +282,10 @@ export class ChatView extends HTMLElement {
282
282
  textArea.focus();
283
283
  }
284
284
 
285
+ setInputValue(text: string): void {
286
+ this.#output.input?.getWidget()?.setInputValue(text);
287
+ }
288
+
285
289
  restoreScrollPosition(): void {
286
290
  if (this.#scrollTop === undefined) {
287
291
  return;
@@ -59,6 +59,8 @@ export const enum StateType {
59
59
  CONVERSATION = 'conversation',
60
60
  }
61
61
 
62
+ const DEFAULT_STATE_TYPE = StateType.PROMPT;
63
+
62
64
  export interface State {
63
65
  activeType: StateType;
64
66
  promptText: string;
@@ -78,9 +80,7 @@ type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;
78
80
  export const DEFAULT_VIEW: View = (input, _output, target): void => {
79
81
  const isPrompt = input.state.activeType === StateType.PROMPT;
80
82
  const buttonText = isPrompt ? i18nString(UIStrings.copyToClipboard) : i18nString(UIStrings.saveAsMarkdown);
81
- const exportText = isPrompt && input.state.isPromptLoading ?
82
- i18nString(UIStrings.generatingSummary) :
83
- (isPrompt ? input.state.promptText : input.state.conversationText);
83
+ const exportText = isPrompt ? input.state.promptText : input.state.conversationText;
84
84
  // clang-format off
85
85
 
86
86
  render(html`
@@ -116,13 +116,13 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
116
116
  </label>
117
117
  </div>
118
118
  <main>
119
- ${input.state.isPromptLoading ? html`
119
+ ${isPrompt && input.state.isPromptLoading ? html`
120
120
  <span class="prompt-loading">
121
121
  <devtools-spinner></devtools-spinner>
122
122
  ${i18nString(UIStrings.generatingSummary)}
123
123
  </span>
124
124
  ` : Lit.nothing}
125
- <textarea readonly .value=${input.state.isPromptLoading ? '' : exportText}></textarea>
125
+ <textarea readonly .value=${isPrompt && input.state.isPromptLoading ? '' : exportText}></textarea>
126
126
  </main>
127
127
  <div class="disclaimer">${i18nString(UIStrings.disclaimer)}</div>
128
128
  <footer>
@@ -144,6 +144,7 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
144
144
  };
145
145
 
146
146
  export class ExportForAgentsDialog extends UI.Widget.VBox {
147
+ static #lastSelectedType: StateType = DEFAULT_STATE_TYPE;
147
148
  readonly #view: View;
148
149
  readonly #dialog: UI.Dialog.Dialog;
149
150
  #state: State;
@@ -160,7 +161,7 @@ export class ExportForAgentsDialog extends UI.Widget.VBox {
160
161
  super();
161
162
  this.#dialog = options.dialog;
162
163
  this.#state = {
163
- activeType: StateType.PROMPT,
164
+ activeType: ExportForAgentsDialog.#lastSelectedType,
164
165
  promptText: typeof options.promptText === 'string' ? options.promptText : '',
165
166
  conversationText: options.markdownText,
166
167
  isPromptLoading: typeof options.promptText !== 'string',
@@ -179,8 +180,13 @@ export class ExportForAgentsDialog extends UI.Widget.VBox {
179
180
  this.requestUpdate();
180
181
  }
181
182
 
183
+ static clearPersistedViewState(): void {
184
+ ExportForAgentsDialog.#lastSelectedType = DEFAULT_STATE_TYPE;
185
+ }
186
+
182
187
  #onStateChange = (newState: StateType): void => {
183
188
  this.#state.activeType = newState;
189
+ ExportForAgentsDialog.#lastSelectedType = newState;
184
190
  this.requestUpdate();
185
191
  };
186
192