chrome-devtools-frontend 1.0.1528866 → 1.0.1529904
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.
- package/front_end/core/common/Gzip.ts +10 -8
- package/front_end/core/host/UserMetrics.ts +2 -1
- package/front_end/core/root/Runtime.ts +10 -0
- package/front_end/core/sdk/NetworkManager.ts +85 -35
- package/front_end/core/sdk/SourceMap.ts +14 -0
- package/front_end/core/sdk/SourceMapScopesInfo.ts +32 -2
- package/front_end/entrypoints/main/MainImpl.ts +23 -4
- package/front_end/generated/SupportedCSSProperties.js +9 -0
- package/front_end/models/ai_assistance/BuiltInAi.ts +1 -1
- package/front_end/models/ai_assistance/ConversationHandler.ts +15 -14
- package/front_end/models/bindings/CompilerScriptMapping.ts +54 -4
- package/front_end/models/javascript_metadata/NativeFunctions.js +8 -0
- package/front_end/models/persistence/NetworkPersistenceManager.ts +3 -5
- package/front_end/models/persistence/PersistenceImpl.ts +0 -5
- package/front_end/models/persistence/persistence-meta.ts +0 -31
- package/front_end/models/persistence/persistence.ts +0 -6
- package/front_end/{models/persistence → panels/common}/PersistenceUtils.ts +15 -17
- package/front_end/panels/common/common.ts +1 -0
- package/front_end/panels/console/ConsoleInsightTeaser.ts +285 -22
- package/front_end/panels/console/ConsolePrompt.ts +10 -2
- package/front_end/panels/console/ConsoleViewMessage.ts +18 -1
- package/front_end/panels/console/console-meta.ts +14 -0
- package/front_end/panels/console/consoleInsightTeaser.css +28 -0
- package/front_end/panels/console/consolePrompt.css +3 -2
- package/front_end/panels/console/consoleView.css +10 -5
- package/front_end/panels/explain/ActionDelegate.ts +3 -0
- package/front_end/panels/explain/explain-meta.ts +7 -0
- package/front_end/panels/network/BlockedURLsPane.ts +139 -36
- package/front_end/panels/network/NetworkLogView.ts +1 -1
- package/front_end/{models/persistence → panels/settings}/EditFileSystemView.ts +2 -6
- package/front_end/panels/settings/WorkspaceSettingsTab.ts +2 -1
- package/front_end/panels/settings/settings.ts +2 -0
- package/front_end/panels/sources/AiCodeCompletionPlugin.ts +9 -4
- package/front_end/{models/persistence → panels/sources}/PersistenceActions.ts +8 -12
- package/front_end/panels/sources/TabbedEditorContainer.ts +2 -1
- package/front_end/panels/sources/sources-meta.ts +15 -0
- package/front_end/panels/sources/sources.ts +2 -0
- package/front_end/panels/timeline/TimelinePanel.ts +2 -3
- package/front_end/panels/utils/utils.ts +2 -1
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/diff/diff_match_patch.js +1 -1
- package/front_end/ui/legacy/ListWidget.ts +2 -2
- package/front_end/ui/legacy/components/object_ui/objectPropertiesSection.css +1 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +9 -0
- package/package.json +1 -1
- /package/front_end/{models/persistence → panels/settings}/editFileSystemView.css +0 -0
|
@@ -7,9 +7,6 @@ import * as Host from '../../core/host/host.js';
|
|
|
7
7
|
import * as Platform from '../../core/platform/platform.js';
|
|
8
8
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
9
9
|
import * as Protocol from '../../generated/protocol.js';
|
|
10
|
-
// TODO(crbug.com/442509324): remove UI dependency
|
|
11
|
-
// eslint-disable-next-line rulesdir/no-imports-in-directory
|
|
12
|
-
import * as UI from '../../ui/legacy/legacy.js';
|
|
13
10
|
import * as Breakpoints from '../breakpoints/breakpoints.js';
|
|
14
11
|
import * as TextUtils from '../text_utils/text_utils.js';
|
|
15
12
|
import * as Workspace from '../workspace/workspace.js';
|
|
@@ -400,8 +397,7 @@ export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrappe
|
|
|
400
397
|
// No overrides folder, set it up
|
|
401
398
|
if (this.#shouldPromptSaveForOverridesDialog(uiSourceCode)) {
|
|
402
399
|
Host.userMetrics.actionTaken(Host.UserMetrics.Action.OverrideContentContextMenuSetup);
|
|
403
|
-
await new Promise<void>(
|
|
404
|
-
resolve => UI.InspectorView.InspectorView.instance().displaySelectOverrideFolderInfobar(resolve));
|
|
400
|
+
await new Promise<void>(resolve => this.dispatchEventToListeners(Events.LOCAL_OVERRIDES_REQUESTED, resolve));
|
|
405
401
|
await IsolatedFileSystemManager.instance().addFileSystem('overrides');
|
|
406
402
|
}
|
|
407
403
|
|
|
@@ -980,12 +976,14 @@ export const enum Events {
|
|
|
980
976
|
PROJECT_CHANGED = 'ProjectChanged',
|
|
981
977
|
REQUEST_FOR_HEADER_OVERRIDES_FILE_CHANGED = 'RequestsForHeaderOverridesFileChanged',
|
|
982
978
|
LOCAL_OVERRIDES_PROJECT_UPDATED = 'LocalOverridesProjectUpdated',
|
|
979
|
+
LOCAL_OVERRIDES_REQUESTED = 'LocalOverridesRequested',
|
|
983
980
|
}
|
|
984
981
|
|
|
985
982
|
export interface EventTypes {
|
|
986
983
|
[Events.PROJECT_CHANGED]: Workspace.Workspace.Project|null;
|
|
987
984
|
[Events.REQUEST_FOR_HEADER_OVERRIDES_FILE_CHANGED]: Workspace.UISourceCode.UISourceCode;
|
|
988
985
|
[Events.LOCAL_OVERRIDES_PROJECT_UPDATED]: boolean;
|
|
986
|
+
[Events.LOCAL_OVERRIDES_REQUESTED]: () => void;
|
|
989
987
|
}
|
|
990
988
|
|
|
991
989
|
export interface HeaderOverride {
|
|
@@ -6,14 +6,12 @@ import * as Common from '../../core/common/common.js';
|
|
|
6
6
|
import * as Host from '../../core/host/host.js';
|
|
7
7
|
import * as Platform from '../../core/platform/platform.js';
|
|
8
8
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
9
|
-
import * as Components from '../../ui/legacy/components/utils/utils.js';
|
|
10
9
|
import * as Bindings from '../bindings/bindings.js';
|
|
11
10
|
import * as BreakpointManager from '../breakpoints/breakpoints.js';
|
|
12
11
|
import * as TextUtils from '../text_utils/text_utils.js';
|
|
13
12
|
import * as Workspace from '../workspace/workspace.js';
|
|
14
13
|
|
|
15
14
|
import {Automapping, type AutomappingStatus} from './Automapping.js';
|
|
16
|
-
import {LinkDecorator} from './PersistenceUtils.js';
|
|
17
15
|
|
|
18
16
|
let persistenceInstance: PersistenceImpl;
|
|
19
17
|
|
|
@@ -33,9 +31,6 @@ export class PersistenceImpl extends Common.ObjectWrapper.ObjectWrapper<EventTyp
|
|
|
33
31
|
this.#breakpointManager = breakpointManager;
|
|
34
32
|
this.#breakpointManager.addUpdateBindingsCallback(this.#setupBindings.bind(this));
|
|
35
33
|
|
|
36
|
-
const linkDecorator = new LinkDecorator(this);
|
|
37
|
-
Components.Linkifier.Linkifier.setLinkDecorator(linkDecorator);
|
|
38
|
-
|
|
39
34
|
this.#mapping = new Automapping(this.#workspace, this.onStatusAdded.bind(this), this.onStatusRemoved.bind(this));
|
|
40
35
|
}
|
|
41
36
|
|
|
@@ -4,13 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Common from '../../core/common/common.js';
|
|
6
6
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
7
|
-
import * as SDK from '../../core/sdk/sdk.js';
|
|
8
|
-
import * as Workspace from '../../models/workspace/workspace.js';
|
|
9
|
-
// TODO(crbug.com/442509324): remove UI dependency
|
|
10
|
-
// eslint-disable-next-line rulesdir/no-imports-in-directory
|
|
11
|
-
import * as UI from '../../ui/legacy/legacy.js';
|
|
12
|
-
|
|
13
|
-
import type * as Persistence from './persistence.js';
|
|
14
7
|
|
|
15
8
|
const UIStrings = {
|
|
16
9
|
/**
|
|
@@ -50,15 +43,6 @@ const UIStrings = {
|
|
|
50
43
|
const str_ = i18n.i18n.registerUIStrings('models/persistence/persistence-meta.ts', UIStrings);
|
|
51
44
|
const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
|
|
52
45
|
|
|
53
|
-
let loadedPersistenceModule: (typeof Persistence|undefined);
|
|
54
|
-
|
|
55
|
-
async function loadPersistenceModule(): Promise<typeof Persistence> {
|
|
56
|
-
if (!loadedPersistenceModule) {
|
|
57
|
-
loadedPersistenceModule = await import('./persistence.js');
|
|
58
|
-
}
|
|
59
|
-
return loadedPersistenceModule;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
46
|
Common.Settings.registerSettingExtension({
|
|
63
47
|
category: Common.Settings.SettingCategory.PERSISTENCE,
|
|
64
48
|
title: i18nLazyString(UIStrings.enableLocalOverrides),
|
|
@@ -83,18 +67,3 @@ Common.Settings.registerSettingExtension({
|
|
|
83
67
|
},
|
|
84
68
|
],
|
|
85
69
|
});
|
|
86
|
-
|
|
87
|
-
UI.ContextMenu.registerProvider({
|
|
88
|
-
contextTypes() {
|
|
89
|
-
return [
|
|
90
|
-
Workspace.UISourceCode.UISourceCode,
|
|
91
|
-
SDK.Resource.Resource,
|
|
92
|
-
SDK.NetworkRequest.NetworkRequest,
|
|
93
|
-
];
|
|
94
|
-
},
|
|
95
|
-
async loadProvider() {
|
|
96
|
-
const Persistence = await loadPersistenceModule();
|
|
97
|
-
return new Persistence.PersistenceActions.ContextMenuProvider();
|
|
98
|
-
},
|
|
99
|
-
experiment: undefined,
|
|
100
|
-
});
|
|
@@ -5,27 +5,21 @@
|
|
|
5
5
|
import * as Automapping from './Automapping.js';
|
|
6
6
|
import * as AutomaticFileSystemManager from './AutomaticFileSystemManager.js';
|
|
7
7
|
import * as AutomaticFileSystemWorkspaceBinding from './AutomaticFileSystemWorkspaceBinding.js';
|
|
8
|
-
import * as EditFileSystemView from './EditFileSystemView.js';
|
|
9
8
|
import * as FileSystemWorkspaceBinding from './FileSystemWorkspaceBinding.js';
|
|
10
9
|
import * as IsolatedFileSystem from './IsolatedFileSystem.js';
|
|
11
10
|
import * as IsolatedFileSystemManager from './IsolatedFileSystemManager.js';
|
|
12
11
|
import * as NetworkPersistenceManager from './NetworkPersistenceManager.js';
|
|
13
|
-
import * as PersistenceActions from './PersistenceActions.js';
|
|
14
12
|
import * as Persistence from './PersistenceImpl.js';
|
|
15
|
-
import * as PersistenceUtils from './PersistenceUtils.js';
|
|
16
13
|
import * as PlatformFileSystem from './PlatformFileSystem.js';
|
|
17
14
|
|
|
18
15
|
export {
|
|
19
16
|
Automapping,
|
|
20
17
|
AutomaticFileSystemManager,
|
|
21
18
|
AutomaticFileSystemWorkspaceBinding,
|
|
22
|
-
EditFileSystemView,
|
|
23
19
|
FileSystemWorkspaceBinding,
|
|
24
20
|
IsolatedFileSystem,
|
|
25
21
|
IsolatedFileSystemManager,
|
|
26
22
|
NetworkPersistenceManager,
|
|
27
23
|
Persistence,
|
|
28
|
-
PersistenceActions,
|
|
29
|
-
PersistenceUtils,
|
|
30
24
|
PlatformFileSystem,
|
|
31
25
|
};
|
|
@@ -6,16 +6,11 @@
|
|
|
6
6
|
import * as Common from '../../core/common/common.js';
|
|
7
7
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
8
8
|
import * as Platform from '../../core/platform/platform.js';
|
|
9
|
+
import * as Persistence from '../../models/persistence/persistence.js';
|
|
10
|
+
import * as Workspace from '../../models/workspace/workspace.js';
|
|
9
11
|
import * as IconButton from '../../ui/components/icon_button/icon_button.js';
|
|
10
12
|
import * as Components from '../../ui/legacy/components/utils/utils.js';
|
|
11
|
-
// TODO(crbug.com/442509324): remove UI dependency
|
|
12
|
-
// eslint-disable-next-line rulesdir/no-imports-in-directory
|
|
13
13
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
14
|
-
import * as Workspace from '../workspace/workspace.js';
|
|
15
|
-
|
|
16
|
-
import {FileSystemWorkspaceBinding} from './FileSystemWorkspaceBinding.js';
|
|
17
|
-
import {NetworkPersistenceManager} from './NetworkPersistenceManager.js';
|
|
18
|
-
import {Events, type PersistenceBinding, PersistenceImpl} from './PersistenceImpl.js';
|
|
19
14
|
|
|
20
15
|
const UIStrings = {
|
|
21
16
|
/**
|
|
@@ -29,16 +24,17 @@ const UIStrings = {
|
|
|
29
24
|
*/
|
|
30
25
|
linkedToS: 'Linked to {PH1}',
|
|
31
26
|
} as const;
|
|
32
|
-
const str_ = i18n.i18n.registerUIStrings('
|
|
27
|
+
const str_ = i18n.i18n.registerUIStrings('panels/common/PersistenceUtils.ts', UIStrings);
|
|
33
28
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
34
29
|
export class PersistenceUtils {
|
|
35
30
|
static tooltipForUISourceCode(uiSourceCode: Workspace.UISourceCode.UISourceCode): string {
|
|
36
|
-
const binding = PersistenceImpl.instance().binding(uiSourceCode);
|
|
31
|
+
const binding = Persistence.Persistence.PersistenceImpl.instance().binding(uiSourceCode);
|
|
37
32
|
if (!binding) {
|
|
38
33
|
return '';
|
|
39
34
|
}
|
|
40
35
|
if (uiSourceCode === binding.network) {
|
|
41
|
-
return FileSystemWorkspaceBinding.tooltipForUISourceCode(
|
|
36
|
+
return Persistence.FileSystemWorkspaceBinding.FileSystemWorkspaceBinding.tooltipForUISourceCode(
|
|
37
|
+
binding.fileSystem);
|
|
42
38
|
}
|
|
43
39
|
if (binding.network.contentType().isFromSourceMap()) {
|
|
44
40
|
return i18nString(
|
|
@@ -48,7 +44,7 @@ export class PersistenceUtils {
|
|
|
48
44
|
}
|
|
49
45
|
|
|
50
46
|
static iconForUISourceCode(uiSourceCode: Workspace.UISourceCode.UISourceCode): IconButton.Icon.Icon|null {
|
|
51
|
-
const binding = PersistenceImpl.instance().binding(uiSourceCode);
|
|
47
|
+
const binding = Persistence.Persistence.PersistenceImpl.instance().binding(uiSourceCode);
|
|
52
48
|
if (binding) {
|
|
53
49
|
if (!Common.ParsedURL.schemeIs(binding.fileSystem.url(), 'file:')) {
|
|
54
50
|
return null;
|
|
@@ -57,7 +53,8 @@ export class PersistenceUtils {
|
|
|
57
53
|
icon.name = 'document';
|
|
58
54
|
icon.classList.add('small');
|
|
59
55
|
UI.Tooltip.Tooltip.install(icon, PersistenceUtils.tooltipForUISourceCode(binding.network));
|
|
60
|
-
if (NetworkPersistenceManager.instance().project() ===
|
|
56
|
+
if (Persistence.NetworkPersistenceManager.NetworkPersistenceManager.instance().project() ===
|
|
57
|
+
binding.fileSystem.project()) {
|
|
61
58
|
icon.classList.add('dot', 'purple');
|
|
62
59
|
} else {
|
|
63
60
|
icon.classList.add('dot', 'green');
|
|
@@ -70,7 +67,8 @@ export class PersistenceUtils {
|
|
|
70
67
|
return null;
|
|
71
68
|
}
|
|
72
69
|
|
|
73
|
-
if (NetworkPersistenceManager.instance().isActiveHeaderOverrides(
|
|
70
|
+
if (Persistence.NetworkPersistenceManager.NetworkPersistenceManager.instance().isActiveHeaderOverrides(
|
|
71
|
+
uiSourceCode)) {
|
|
74
72
|
const icon = new IconButton.Icon.Icon();
|
|
75
73
|
icon.name = 'document';
|
|
76
74
|
icon.classList.add('small');
|
|
@@ -88,13 +86,13 @@ export class PersistenceUtils {
|
|
|
88
86
|
|
|
89
87
|
export class LinkDecorator extends Common.ObjectWrapper.ObjectWrapper<Components.Linkifier.LinkDecorator.EventTypes>
|
|
90
88
|
implements Components.Linkifier.LinkDecorator {
|
|
91
|
-
constructor(persistence: PersistenceImpl) {
|
|
89
|
+
constructor(persistence: Persistence.Persistence.PersistenceImpl) {
|
|
92
90
|
super();
|
|
93
|
-
persistence.addEventListener(Events.BindingCreated, this.bindingChanged, this);
|
|
94
|
-
persistence.addEventListener(Events.BindingRemoved, this.bindingChanged, this);
|
|
91
|
+
persistence.addEventListener(Persistence.Persistence.Events.BindingCreated, this.bindingChanged, this);
|
|
92
|
+
persistence.addEventListener(Persistence.Persistence.Events.BindingRemoved, this.bindingChanged, this);
|
|
95
93
|
}
|
|
96
94
|
|
|
97
|
-
private bindingChanged(event: Common.EventTarget.EventTargetEvent<PersistenceBinding>): void {
|
|
95
|
+
private bindingChanged(event: Common.EventTarget.EventTargetEvent<Persistence.Persistence.PersistenceBinding>): void {
|
|
98
96
|
const binding = event.data;
|
|
99
97
|
this.dispatchEventToListeners(Components.Linkifier.LinkDecorator.Events.LINK_ICON_CHANGED, binding.network);
|
|
100
98
|
}
|
|
@@ -100,3 +100,4 @@ export {GdpSignUpDialog} from './GdpSignUpDialog.js';
|
|
|
100
100
|
export {AiCodeCompletionDisclaimer} from './AiCodeCompletionDisclaimer.js';
|
|
101
101
|
export {AiCodeCompletionSummaryToolbar} from './AiCodeCompletionSummaryToolbar.js';
|
|
102
102
|
export * from './BadgeNotification.js';
|
|
103
|
+
export * as PersistenceUtils from './PersistenceUtils.js';
|
|
@@ -4,16 +4,55 @@
|
|
|
4
4
|
|
|
5
5
|
import '../../ui/components/tooltips/tooltips.js';
|
|
6
6
|
|
|
7
|
+
import * as Common from '../../core/common/common.js';
|
|
8
|
+
import * as Host from '../../core/host/host.js';
|
|
7
9
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
10
|
+
import * as Root from '../../core/root/root.js';
|
|
11
|
+
import * as AiAssistanceModel from '../../models/ai_assistance/ai_assistance.js';
|
|
12
|
+
import * as Buttons from '../../ui/components/buttons/buttons.js';
|
|
8
13
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
9
14
|
import * as Lit from '../../ui/lit/lit.js';
|
|
15
|
+
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
16
|
+
import * as PanelCommon from '../common/common.js';
|
|
10
17
|
|
|
11
18
|
import consoleInsightTeaserStyles from './consoleInsightTeaser.css.js';
|
|
12
|
-
import
|
|
19
|
+
import {ConsoleViewMessage} from './ConsoleViewMessage.js';
|
|
20
|
+
import {PromptBuilder} from './PromptBuilder.js';
|
|
13
21
|
|
|
14
22
|
const {render, html} = Lit;
|
|
15
23
|
|
|
16
24
|
const UIStringsNotTranslate = {
|
|
25
|
+
/**
|
|
26
|
+
* @description Link text in the disclaimer dialog, linking to a settings page containing more information
|
|
27
|
+
*/
|
|
28
|
+
learnMore: 'Learn more about AI summaries',
|
|
29
|
+
/**
|
|
30
|
+
* @description Description of the console insights feature
|
|
31
|
+
*/
|
|
32
|
+
freDisclaimerHeader: 'Get explanations for console warnings and errors',
|
|
33
|
+
/**
|
|
34
|
+
* @description First item in the first-run experience dialog
|
|
35
|
+
*/
|
|
36
|
+
freDisclaimerTextAiWontAlwaysGetItRight: 'This feature uses AI and won’t always get it right',
|
|
37
|
+
/**
|
|
38
|
+
* @description Explainer for which data is being sent by the console insights feature
|
|
39
|
+
*/
|
|
40
|
+
consoleInsightsSendsData:
|
|
41
|
+
'To generate explanations, the console message, associated stack trace, related source code, and the associated network headers are sent to Google. This data may be seen by human reviewers to improve this feature.',
|
|
42
|
+
/**
|
|
43
|
+
* @description Explainer for which data is being sent by the console insights feature
|
|
44
|
+
*/
|
|
45
|
+
consoleInsightsSendsDataNoLogging:
|
|
46
|
+
'To generate explanations, the console message, associated stack trace, related source code, and the associated network headers are sent to Google. This data will not be used to improve Google’s AI models. Your organization may change these settings at any time.',
|
|
47
|
+
/**
|
|
48
|
+
* @description Third item in the first-run experience dialog
|
|
49
|
+
*/
|
|
50
|
+
freDisclaimerTextUseWithCaution: 'Use generated code snippets with caution',
|
|
51
|
+
/**
|
|
52
|
+
* @description Tooltip text for the console insights teaser
|
|
53
|
+
*/
|
|
54
|
+
infoTooltipText:
|
|
55
|
+
'The text above has been generated with AI on your local device. Clicking the button will send the console message, stack trace, related source code, and the associated network headers to Google to generate a more detailed explanation.',
|
|
17
56
|
/**
|
|
18
57
|
* @description Header text during loading state while an AI summary is being generated
|
|
19
58
|
*/
|
|
@@ -22,15 +61,32 @@ const UIStringsNotTranslate = {
|
|
|
22
61
|
* @description Label for an animation shown while an AI response is being generated
|
|
23
62
|
*/
|
|
24
63
|
loading: 'Loading',
|
|
64
|
+
/**
|
|
65
|
+
* @description Label for a button which generates a more detailed explanation
|
|
66
|
+
*/
|
|
67
|
+
tellMeMore: 'Tell me more',
|
|
68
|
+
/**
|
|
69
|
+
* @description Label for a checkbox which turns off the teaser explanation feature
|
|
70
|
+
*/
|
|
71
|
+
dontShow: 'Don’t show',
|
|
25
72
|
} as const;
|
|
26
73
|
|
|
27
74
|
const lockedString = i18n.i18n.lockedString;
|
|
28
75
|
|
|
76
|
+
const CODE_SNIPPET_WARNING_URL = 'https://support.google.com/legal/answer/13505487';
|
|
77
|
+
const DATA_USAGE_URL = 'https://developer.chrome.com/docs/devtools/ai-assistance/get-started#data-use';
|
|
78
|
+
const EXPLAIN_TEASER_ACTION_ID = 'explain.console-message.teaser';
|
|
79
|
+
|
|
29
80
|
interface ViewInput {
|
|
81
|
+
onTellMeMoreClick: (event: Event) => void;
|
|
30
82
|
// If multiple ConsoleInsightTeasers exist, each one needs a unique id. Otherwise showing and
|
|
31
83
|
// hiding of the tooltip, and rendering the loading animation, does not work correctly.
|
|
32
|
-
uuid:
|
|
84
|
+
uuid: string;
|
|
85
|
+
headerText: string;
|
|
86
|
+
mainText: string;
|
|
33
87
|
isInactive: boolean;
|
|
88
|
+
dontShowChanged: (e: Event) => void;
|
|
89
|
+
hasTellMeMoreButton: boolean;
|
|
34
90
|
}
|
|
35
91
|
|
|
36
92
|
export const DEFAULT_VIEW = (input: ViewInput, _output: undefined, target: HTMLElement): void => {
|
|
@@ -39,6 +95,7 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: undefined, target: HTMLE
|
|
|
39
95
|
return;
|
|
40
96
|
}
|
|
41
97
|
|
|
98
|
+
const showPlaceholder = !Boolean(input.mainText);
|
|
42
99
|
// clang-format off
|
|
43
100
|
render(html`
|
|
44
101
|
<style>${consoleInsightTeaserStyles}</style>
|
|
@@ -50,23 +107,62 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: undefined, target: HTMLE
|
|
|
50
107
|
prefer-span-left
|
|
51
108
|
>
|
|
52
109
|
<div class="teaser-tooltip-container">
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<
|
|
62
|
-
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
110
|
+
${showPlaceholder ? html`
|
|
111
|
+
<h2 tabindex="-1">${lockedString(UIStringsNotTranslate.summarizing)}</h2>
|
|
112
|
+
<div
|
|
113
|
+
role="presentation"
|
|
114
|
+
aria-label=${lockedString(UIStringsNotTranslate.loading)}
|
|
115
|
+
class="loader"
|
|
116
|
+
style="clip-path: url(${'#clipPath-' + input.uuid});"
|
|
117
|
+
>
|
|
118
|
+
<svg width="100%" height="52">
|
|
119
|
+
<defs>
|
|
120
|
+
<clipPath id=${'clipPath-' + input.uuid}>
|
|
121
|
+
<rect x="0" y="0" width="100%" height="12" rx="8"></rect>
|
|
122
|
+
<rect x="0" y="20" width="100%" height="12" rx="8"></rect>
|
|
123
|
+
<rect x="0" y="40" width="100%" height="12" rx="8"></rect>
|
|
124
|
+
</clipPath>
|
|
125
|
+
</defs>
|
|
126
|
+
</svg>
|
|
127
|
+
</div>
|
|
128
|
+
` : html`
|
|
129
|
+
<h2 tabindex="-1">${input.headerText}</h2>
|
|
130
|
+
<div>${input.mainText}</div>
|
|
131
|
+
<div class="tooltip-footer">
|
|
132
|
+
${input.hasTellMeMoreButton ? html`
|
|
133
|
+
<devtools-button
|
|
134
|
+
title=${lockedString(UIStringsNotTranslate.tellMeMore)}
|
|
135
|
+
.jslogContext=${'insights-teaser-tell-me-more'},
|
|
136
|
+
.variant=${Buttons.Button.Variant.PRIMARY}
|
|
137
|
+
@click=${input.onTellMeMoreClick}
|
|
138
|
+
>
|
|
139
|
+
<devtools-icon class="lightbulb-icon" name="lightbulb-spark"></devtools-icon>
|
|
140
|
+
${lockedString(UIStringsNotTranslate.tellMeMore)}
|
|
141
|
+
</devtools-button>
|
|
142
|
+
` : Lit.nothing}
|
|
143
|
+
<devtools-icon
|
|
144
|
+
name="info"
|
|
145
|
+
class="info-icon"
|
|
146
|
+
aria-details=${'teaser-info-tooltip-' + input.uuid}
|
|
147
|
+
></devtools-icon>
|
|
148
|
+
<devtools-tooltip id=${'teaser-info-tooltip-' + input.uuid} variant="rich">
|
|
149
|
+
<div class="info-tooltip-text">${lockedString(UIStringsNotTranslate.infoTooltipText)}</div>
|
|
150
|
+
<div class="learn-more">
|
|
151
|
+
<x-link
|
|
152
|
+
class="devtools-link"
|
|
153
|
+
title=${lockedString(UIStringsNotTranslate.learnMore)}
|
|
154
|
+
href=${DATA_USAGE_URL}
|
|
155
|
+
jslog=${VisualLogging.link().track({click: true, keydown:'Enter|Space'}).context('explain.teaser.learn-more')}
|
|
156
|
+
>${lockedString(UIStringsNotTranslate.learnMore)}</x-link>
|
|
157
|
+
</div>
|
|
158
|
+
</devtools-tooltip>
|
|
159
|
+
<devtools-checkbox
|
|
160
|
+
@change=${input.dontShowChanged}
|
|
161
|
+
jslog=${VisualLogging.toggle('explain.teaser.dont-show').track({ change: true })}>
|
|
162
|
+
${lockedString(UIStringsNotTranslate.dontShow)}
|
|
163
|
+
</devtools-checkbox>
|
|
164
|
+
</div>
|
|
165
|
+
`}
|
|
70
166
|
</div>
|
|
71
167
|
</devtools-tooltip>
|
|
72
168
|
`, target);
|
|
@@ -77,16 +173,110 @@ export type View = typeof DEFAULT_VIEW;
|
|
|
77
173
|
|
|
78
174
|
export class ConsoleInsightTeaser extends UI.Widget.Widget {
|
|
79
175
|
#view: View;
|
|
80
|
-
#uuid:
|
|
176
|
+
#uuid: string;
|
|
177
|
+
#isGenerating = false;
|
|
178
|
+
#builtInAi: AiAssistanceModel.BuiltInAi.BuiltInAi|undefined;
|
|
179
|
+
#promptBuilder: PromptBuilder;
|
|
180
|
+
#headerText = '';
|
|
181
|
+
#mainText = '';
|
|
182
|
+
#consoleViewMessage: ConsoleViewMessage;
|
|
81
183
|
#isInactive = false;
|
|
184
|
+
#abortController: null|AbortController = null;
|
|
82
185
|
|
|
83
|
-
constructor(uuid:
|
|
186
|
+
constructor(uuid: string, consoleViewMessage: ConsoleViewMessage, element?: HTMLElement, view?: View) {
|
|
84
187
|
super(element);
|
|
85
188
|
this.#view = view ?? DEFAULT_VIEW;
|
|
86
189
|
this.#uuid = uuid;
|
|
190
|
+
this.#promptBuilder = new PromptBuilder(consoleViewMessage);
|
|
191
|
+
this.#consoleViewMessage = consoleViewMessage;
|
|
87
192
|
this.requestUpdate();
|
|
88
193
|
}
|
|
89
194
|
|
|
195
|
+
#getConsoleInsightsEnabledSetting(): Common.Settings.Setting<boolean>|undefined {
|
|
196
|
+
try {
|
|
197
|
+
return Common.Settings.moduleSetting('console-insights-enabled') as Common.Settings.Setting<boolean>;
|
|
198
|
+
} catch {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
#getOnboardingCompletedSetting(): Common.Settings.Setting<boolean> {
|
|
204
|
+
return Common.Settings.Settings.instance().createLocalSetting('console-insights-onboarding-finished', true);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
#executeConsoleInsightAction(): void {
|
|
208
|
+
UI.Context.Context.instance().setFlavor(ConsoleViewMessage, this.#consoleViewMessage);
|
|
209
|
+
const action = UI.ActionRegistry.ActionRegistry.instance().getAction(EXPLAIN_TEASER_ACTION_ID);
|
|
210
|
+
void action.execute();
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
#onTellMeMoreClick(event: Event): void {
|
|
214
|
+
event.stopPropagation();
|
|
215
|
+
if (this.#getConsoleInsightsEnabledSetting()?.getIfNotDisabled() &&
|
|
216
|
+
this.#getOnboardingCompletedSetting()?.getIfNotDisabled()) {
|
|
217
|
+
this.#executeConsoleInsightAction();
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
void this.#showFreDialog();
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async #showFreDialog(): Promise<void> {
|
|
224
|
+
const noLogging = Root.Runtime.hostConfig.aidaAvailability?.enterprisePolicyValue ===
|
|
225
|
+
Root.Runtime.GenAiEnterprisePolicyValue.ALLOW_WITHOUT_LOGGING;
|
|
226
|
+
const result = await PanelCommon.FreDialog.show({
|
|
227
|
+
header: {iconName: 'smart-assistant', text: lockedString(UIStringsNotTranslate.freDisclaimerHeader)},
|
|
228
|
+
reminderItems: [
|
|
229
|
+
{
|
|
230
|
+
iconName: 'psychiatry',
|
|
231
|
+
content: lockedString(UIStringsNotTranslate.freDisclaimerTextAiWontAlwaysGetItRight),
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
iconName: 'google',
|
|
235
|
+
content: noLogging ? lockedString(UIStringsNotTranslate.consoleInsightsSendsDataNoLogging) :
|
|
236
|
+
lockedString(UIStringsNotTranslate.consoleInsightsSendsData),
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
iconName: 'warning',
|
|
240
|
+
// clang-format off
|
|
241
|
+
content: html`<x-link
|
|
242
|
+
href=${CODE_SNIPPET_WARNING_URL}
|
|
243
|
+
class="link devtools-link"
|
|
244
|
+
jslog=${VisualLogging.link('explain.teaser.code-snippets-explainer').track({
|
|
245
|
+
click: true
|
|
246
|
+
})}
|
|
247
|
+
>${lockedString(UIStringsNotTranslate.freDisclaimerTextUseWithCaution)}</x-link>`,
|
|
248
|
+
// clang-format on
|
|
249
|
+
}
|
|
250
|
+
],
|
|
251
|
+
onLearnMoreClick: () => {
|
|
252
|
+
void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
|
|
253
|
+
},
|
|
254
|
+
ariaLabel: lockedString(UIStringsNotTranslate.freDisclaimerHeader),
|
|
255
|
+
learnMoreButtonText: lockedString(UIStringsNotTranslate.learnMore),
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
if (result) {
|
|
259
|
+
this.#getConsoleInsightsEnabledSetting()?.set(true);
|
|
260
|
+
this.#getOnboardingCompletedSetting()?.set(true);
|
|
261
|
+
this.#executeConsoleInsightAction();
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
maybeGenerateTeaser(): void {
|
|
266
|
+
this.requestUpdate();
|
|
267
|
+
if (!this.#isInactive && !this.#isGenerating && !Boolean(this.#mainText) &&
|
|
268
|
+
Common.Settings.Settings.instance().moduleSetting('console-insight-teasers-enabled').get()) {
|
|
269
|
+
void this.#generateTeaserText();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
abortTeaserGeneration(): void {
|
|
274
|
+
if (this.#abortController) {
|
|
275
|
+
this.#abortController.abort();
|
|
276
|
+
}
|
|
277
|
+
this.#isGenerating = false;
|
|
278
|
+
}
|
|
279
|
+
|
|
90
280
|
setInactive(isInactive: boolean): void {
|
|
91
281
|
if (this.#isInactive === isInactive) {
|
|
92
282
|
return;
|
|
@@ -95,11 +285,84 @@ export class ConsoleInsightTeaser extends UI.Widget.Widget {
|
|
|
95
285
|
this.requestUpdate();
|
|
96
286
|
}
|
|
97
287
|
|
|
288
|
+
async #generateTeaserText(): Promise<void> {
|
|
289
|
+
this.#isGenerating = true;
|
|
290
|
+
let teaserText = '';
|
|
291
|
+
try {
|
|
292
|
+
for await (const chunk of this.#getOnDeviceInsight()) {
|
|
293
|
+
teaserText += chunk;
|
|
294
|
+
}
|
|
295
|
+
} catch (err) {
|
|
296
|
+
// Ignore `AbortError` errors, which are thrown on mouse leave.
|
|
297
|
+
if (err.name !== 'AbortError') {
|
|
298
|
+
console.error(err.name, err.message);
|
|
299
|
+
}
|
|
300
|
+
this.#isGenerating = false;
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// TODO(crbug.com/443618746): Add user-facing error message instead of staying in loading state
|
|
305
|
+
let responseObject = {
|
|
306
|
+
header: null,
|
|
307
|
+
explanation: null,
|
|
308
|
+
};
|
|
309
|
+
try {
|
|
310
|
+
responseObject = JSON.parse(teaserText);
|
|
311
|
+
} catch (err) {
|
|
312
|
+
console.error(err.name, err.message);
|
|
313
|
+
}
|
|
314
|
+
this.#headerText = responseObject.header || '';
|
|
315
|
+
this.#mainText = responseObject.explanation || '';
|
|
316
|
+
this.#isGenerating = false;
|
|
317
|
+
this.requestUpdate();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async * #getOnDeviceInsight(): AsyncGenerator<string> {
|
|
321
|
+
const {prompt} = await this.#promptBuilder.buildPrompt();
|
|
322
|
+
if (!this.#builtInAi) {
|
|
323
|
+
this.#builtInAi = await AiAssistanceModel.BuiltInAi.BuiltInAi.instance();
|
|
324
|
+
if (!this.#builtInAi) {
|
|
325
|
+
this.#isInactive = true;
|
|
326
|
+
throw new Error('Cannot instantiate BuiltInAi');
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
this.#abortController = new AbortController();
|
|
330
|
+
const stream = this.#builtInAi.getConsoleInsight(prompt, this.#abortController);
|
|
331
|
+
for await (const chunk of stream) {
|
|
332
|
+
yield chunk;
|
|
333
|
+
}
|
|
334
|
+
this.#abortController = null;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
#dontShowChanged(e: Event): void {
|
|
338
|
+
const showTeasers = !(e.target as HTMLInputElement).checked;
|
|
339
|
+
Common.Settings.Settings.instance().moduleSetting('console-insight-teasers-enabled').set(showTeasers);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
#hasTellMeMoreButton(): boolean {
|
|
343
|
+
if (!UI.ActionRegistry.ActionRegistry.instance().hasAction(EXPLAIN_TEASER_ACTION_ID)) {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
if (Root.Runtime.hostConfig.aidaAvailability?.blockedByAge || Root.Runtime.hostConfig.isOffTheRecord) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
if (!Host.AidaClient.AidaAccessPreconditions.AVAILABLE) {
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
return true;
|
|
353
|
+
}
|
|
354
|
+
|
|
98
355
|
override performUpdate(): Promise<void>|void {
|
|
99
356
|
this.#view(
|
|
100
357
|
{
|
|
358
|
+
onTellMeMoreClick: this.#onTellMeMoreClick.bind(this),
|
|
101
359
|
uuid: this.#uuid,
|
|
102
|
-
|
|
360
|
+
headerText: this.#headerText,
|
|
361
|
+
mainText: this.#mainText,
|
|
362
|
+
isInactive: this.#isInactive ||
|
|
363
|
+
!Common.Settings.Settings.instance().moduleSetting('console-insight-teasers-enabled').get(),
|
|
364
|
+
dontShowChanged: this.#dontShowChanged.bind(this),
|
|
365
|
+
hasTellMeMoreButton: this.#hasTellMeMoreButton(),
|
|
103
366
|
},
|
|
104
367
|
undefined, this.contentElement);
|
|
105
368
|
}
|
|
@@ -170,8 +170,7 @@ export class ConsolePrompt extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
|
170
170
|
Common.Settings.Settings.instance().createSetting('ai-code-completion-teaser-dismissed', false);
|
|
171
171
|
if (!this.aiCodeCompletionSetting.get() && !aiCodeCompletionTeaserDismissedSetting.get()) {
|
|
172
172
|
this.teaser = new PanelCommon.AiCodeCompletionTeaser({onDetach: this.detachAiCodeCompletionTeaser.bind(this)});
|
|
173
|
-
extensions.push(this.placeholderCompartment.of(
|
|
174
|
-
TextEditor.AiCodeCompletionTeaserPlaceholder.aiCodeCompletionTeaserPlaceholder(this.teaser)));
|
|
173
|
+
extensions.push(this.placeholderCompartment.of([]));
|
|
175
174
|
}
|
|
176
175
|
extensions.push(TextEditor.Config.aiAutoCompleteSuggestion);
|
|
177
176
|
}
|
|
@@ -580,9 +579,18 @@ export class ConsolePrompt extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
|
580
579
|
this.aidaAvailability = currentAidaAvailability;
|
|
581
580
|
if (this.aidaAvailability === Host.AidaClient.AidaAccessPreconditions.AVAILABLE) {
|
|
582
581
|
this.onAiCodeCompletionSettingChanged();
|
|
582
|
+
if (this.teaser) {
|
|
583
|
+
this.editor.dispatch({
|
|
584
|
+
effects: this.placeholderCompartment.reconfigure(
|
|
585
|
+
[TextEditor.AiCodeCompletionTeaserPlaceholder.aiCodeCompletionTeaserPlaceholder(this.teaser)])
|
|
586
|
+
});
|
|
587
|
+
}
|
|
583
588
|
} else if (this.aiCodeCompletion) {
|
|
584
589
|
this.aiCodeCompletion.remove();
|
|
585
590
|
this.aiCodeCompletion = undefined;
|
|
591
|
+
if (this.teaser) {
|
|
592
|
+
this.detachAiCodeCompletionTeaser();
|
|
593
|
+
}
|
|
586
594
|
}
|
|
587
595
|
}
|
|
588
596
|
}
|