Add the relevant data disclaimer to the permission prompt · ChromeDevTools/devtools-frontend@02355b5 · GitHub
Skip to content

Commit 02355b5

Browse files
Maxim Sheshukovdevtools-frontend-scoped@luci-project-accounts.iam.gserviceaccount.com
authored andcommitted
Add the relevant data disclaimer to the permission prompt
This CL show the correct "Relevant data is sent to Google" disclaimer in the permission prompt. The disclaimer is moved to a separate module from ChatInput and reused in PermissionPrompt. Bug: 559101869 Change-Id: Ib3ec2ef1dac6f52380841002e04596fdde1f1f12 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/8525367 Reviewed-by: Jack Franklin <jacktfranklin@chromium.org> Commit-Queue: Maxim Sheshukov <maximsheshukov@chromium.org>
1 parent 9151a64 commit 02355b5

15 files changed

Lines changed: 189 additions & 101 deletions

‎config/gni/devtools_grd_files.gni‎

Lines changed: 2 additions & 0 deletions

‎front_end/panels/ai_assistance/BUILD.gn‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ generate_css("css_files") {
1919
"./components/exportForAgentsDialog.css",
2020
"./components/optInChangeDialog.css",
2121
"./components/permissionPrompt.css",
22+
"./components/relevantDataDisclaimer.css",
2223
"./components/walkthroughView.css",
2324
]
2425
}
@@ -41,6 +42,7 @@ devtools_ui_module("ai_assistance") {
4142
"components/OptInChangeDialog.ts",
4243
"components/PerformanceAgentMarkdownRenderer.ts",
4344
"components/PermissionPrompt.ts",
45+
"components/RelevantDataDisclaimer.ts",
4446
"components/WalkthroughUtils.ts",
4547
"components/WalkthroughView.ts",
4648
]
@@ -146,6 +148,7 @@ devtools_ui_module("unittests") {
146148
"components/MarkdownRendererWithCodeBlock.test.ts",
147149
"components/OptInChangeDialog.test.ts",
148150
"components/PermissionPrompt.test.ts",
151+
"components/RelevantDataDisclaimer.test.ts",
149152
"components/WalkthroughUtils.test.ts",
150153
"components/WalkthroughView.test.ts",
151154
]

‎front_end/panels/ai_assistance/ai_assistance.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export * as ExploreWidget from './components/ExploreWidget.js';
1313
export * as ExportForAgentsDialog from './components/ExportForAgentsDialog.js';
1414
export * as OptInChangeDialog from './components/OptInChangeDialog.js';
1515
export * as PermissionPrompt from './components/PermissionPrompt.js';
16+
export * as RelevantDataDisclaimer from './components/RelevantDataDisclaimer.js';
1617
export * as DisabledWidget from './components/DisabledWidget.js';
1718
export * as WalkthroughUtils from './components/WalkthroughUtils.js';
1819
export * as WalkthroughView from './components/WalkthroughView.js';

‎front_end/panels/ai_assistance/components/ChatInput.ts‎

Lines changed: 5 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import '../../../ui/components/tooltips/tooltips.js';
6-
75
import type * as Host from '../../../core/host/host.js';
86
import * as i18n from '../../../core/i18n/i18n.js';
97
import type * as Platform from '../../../core/platform/platform.js';
@@ -21,6 +19,7 @@ import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
2119

2220
import chatInputStyles from './chatInput.css.js';
2321
import * as ImageResize from './ImageResize.js';
22+
import {renderRelevantDataDisclaimer} from './RelevantDataDisclaimer.js';
2423

2524
const {html, Directives: {createRef, ref}} = Lit;
2625
const {widget} = UI.Widget;
@@ -34,10 +33,6 @@ const UIStrings = {
3433
* @description Label added to the button that reveals the selected context item in DevTools.
3534
*/
3635
revealContextDescription: 'Reveal the selected context item in DevTools',
37-
/**
38-
* @description The footer disclaimer that links to more information about the AI feature.
39-
*/
40-
learnAbout: 'Learn about AI in DevTools',
4136
} as const;
4237

4338
/*
@@ -207,45 +202,12 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
207202
disabled: input.isTextInputDisabled,
208203
});
209204

210-
const renderRelevantDataDisclaimer = (tooltipId: string): Lit.LitTemplate => {
205+
const renderDisclaimer = (tooltipId: string): Lit.LitTemplate => {
211206
const classes = Lit.Directives.classMap({
212207
'chat-input-disclaimer': true,
213208
'hide-divider': !input.isLoading && input.blockedByCrossOrigin,
214209
});
215-
// clang-format off
216-
return html`
217-
<div class=${classes}>
218-
<button
219-
class="link"
220-
role="link"
221-
aria-details=${tooltipId}
222-
jslog=${VisualLogging.link('open-ai-settings').track({
223-
click: true,
224-
})}
225-
@click=${(ev: Event) => {
226-
ev.preventDefault();
227-
void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
228-
}}
229-
>${lockedString('Relevant data')}</button>&nbsp;${lockedString('is sent to Google')}
230-
<devtools-tooltip
231-
id=${tooltipId}
232-
variant="rich"
233-
><div class="info-tooltip-container">
234-
${input.disclaimerText}
235-
<button
236-
class="link tooltip-link"
237-
role="link"
238-
jslog=${VisualLogging.link('open-ai-settings').track({
239-
click: true,
240-
})}
241-
@click=${() => {
242-
void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
243-
}}>${i18nString(UIStrings.learnAbout)}
244-
</button>
245-
</div></devtools-tooltip>
246-
</div>
247-
`;
248-
// clang-format on
210+
return html`<div class=${classes}>${renderRelevantDataDisclaimer(tooltipId, input.disclaimerText)}</div>`;
249211
};
250212

251213
// clang-format off
@@ -413,7 +375,7 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
413375
</div>
414376
<div class="chat-input-actions-right">
415377
<div class="chat-input-disclaimer-container">
416-
${renderRelevantDataDisclaimer(RELEVANT_DATA_LINK_CHAT_ID)}
378+
${renderDisclaimer(RELEVANT_DATA_LINK_CHAT_ID)}
417379
</div>
418380
${(input.multimodalInputEnabled && !input.blockedByCrossOrigin) ?
419381
html`
@@ -503,7 +465,7 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
503465
})}
504466
jslog=${VisualLogging.section('footer')}
505467
>
506-
${renderRelevantDataDisclaimer(RELEVANT_DATA_LINK_FOOTER_ID)}
468+
${renderDisclaimer(RELEVANT_DATA_LINK_FOOTER_ID)}
507469
</footer>
508470
`, target);
509471
// clang-format on

‎front_end/panels/ai_assistance/components/ChatView.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ const DEFAULT_VIEW: View = (input, output, target) => {
214214
</div>
215215
`}
216216
${awaitingApprovalStep ? html`
217-
<div class=${inputWidgetClasses}>${renderPermissionPrompt(awaitingApprovalStep)}</div>
217+
<div class=${inputWidgetClasses}>${renderPermissionPrompt(awaitingApprovalStep, input.disclaimerText)}</div>
218218
` : html`<devtools-widget class=${inputWidgetClasses} ${widget(ChatInput, {
219219
isLoading: input.isLoading,
220220
blockedByCrossOrigin: input.blockedByCrossOrigin,

‎front_end/panels/ai_assistance/components/PermissionPrompt.test.ts‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ describeWithEnvironment('PermissionPrompt', () => {
4040
}
4141

4242
function renderPrompt(step: AiAssistance.ChatMessage.Step, target = document.createElement('div')): HTMLElement {
43-
Lit.render(
44-
html`<style>${permissionPromptStyles}</style>${AiAssistance.PermissionPrompt.renderPermissionPrompt(step)}`,
45-
target);
43+
Lit.render(html`<style>${permissionPromptStyles}</style>${
44+
AiAssistance.PermissionPrompt.renderPermissionPrompt(step, 'Disclaimer text')}`,
45+
target);
4646
return target;
4747
}
4848

@@ -107,8 +107,9 @@ describeWithEnvironment('PermissionPrompt', () => {
107107
const target = renderPrompt(createStep());
108108

109109
const footer = querySelectorErrorOnMissing(target, '.permission-prompt-footer');
110-
assert.strictEqual(footer.textContent?.trim(),
111-
'Relevant data is sent to Google. Change permissions in settings at any time.');
110+
assert.strictEqual(footer.querySelector('button.link')?.textContent, 'Relevant data');
111+
assert.include(footer.textContent, 'is sent to Google');
112+
assert.include(footer.textContent, '. Change permissions in settings at any time');
112113
});
113114

114115
it('renders the ALLOW_ONCE prompt', async () => {

‎front_end/panels/ai_assistance/components/PermissionPrompt.ts‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,13 @@ import * as Lit from '../../../ui/lit/lit.js';
1212
import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
1313

1414
import type {Step} from './ChatMessage.js';
15+
import {renderRelevantDataDisclaimer} from './RelevantDataDisclaimer.js';
1516

1617
const {html} = Lit;
1718
const lockedString = i18n.i18n.lockedString;
1819

20+
const RELEVANT_DATA_TOOLTIP_ID = 'relevant-data-link-permission-prompt';
21+
1922
const UIStringsNotTranslate = {
2023
/**
2124
* @description Button text in the permission prompt that skips the tool call.
@@ -34,9 +37,9 @@ const UIStringsNotTranslate = {
3437
*/
3538
allowThisAction: 'Allow this action?',
3639
/**
37-
* @description Footer of the permission prompt.
40+
* @description Second sentence of the permission prompt disclaimer. Follows "Relevant data is sent to Google."
3841
*/
39-
relevantDataIsSentToGoogle: 'Relevant data is sent to Google. Change permissions in settings at any time.',
42+
changePermissionsInSettings: 'Change permissions in settings at any time',
4043
/**
4144
* @description Heading text for the code block that shows the code to be executed after the user allows it.
4245
*/
@@ -58,7 +61,7 @@ const UIStringsNotTranslate = {
5861
* TODO(b/516828269): Remove `renderSideEffectConfirmationUi` once the natural
5962
* language interface launches.
6063
*/
61-
export function renderPermissionPrompt(step: Step): Lit.LitTemplate {
64+
export function renderPermissionPrompt(step: Step, disclaimerText: string): Lit.LitTemplate {
6265
if (step.state.type !== 'needs_approval') {
6366
return Lit.nothing;
6467
}
@@ -114,7 +117,9 @@ export function renderPermissionPrompt(step: Step): Lit.LitTemplate {
114117
@click=${() => dialog.onAnswer(AiAssistanceModel.Tool.PermissionDecision.ALLOW_ONCE)}
115118
>${lockedString(UIStringsNotTranslate.allowThisTime)}</devtools-button>
116119
</div>
117-
<p class="permission-prompt-footer">${lockedString(UIStringsNotTranslate.relevantDataIsSentToGoogle)}</p>
120+
<div class="permission-prompt-footer">
121+
${renderRelevantDataDisclaimer(RELEVANT_DATA_TOOLTIP_ID, disclaimerText)}. ${lockedString(UIStringsNotTranslate.changePermissionsInSettings)}.
122+
</div>
118123
</div>`;
119124
// clang-format on
120125
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2026 The Chromium Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import {assert} from 'chai';
6+
import {it} from 'mocha';
7+
import sinon from 'sinon';
8+
9+
import {querySelectorErrorOnMissing, renderElementIntoDOM} from '../../../testing/DOMHelpers.js';
10+
import {describeWithEnvironment} from '../../../testing/EnvironmentHelpers.js';
11+
import * as UI from '../../../ui/legacy/legacy.js';
12+
import * as Lit from '../../../ui/lit/lit.js';
13+
import * as AiAssistance from '../ai_assistance.js';
14+
15+
describeWithEnvironment('RelevantDataDisclaimer', () => {
16+
function renderDisclaimer(): HTMLElement {
17+
const target = document.createElement('div');
18+
renderElementIntoDOM(target);
19+
Lit.render(AiAssistance.RelevantDataDisclaimer.renderRelevantDataDisclaimer('test-tooltip', 'Disclaimer text'),
20+
target);
21+
return target;
22+
}
23+
24+
it('renders "Relevant data" as a link followed by the rest of the sentence', () => {
25+
const target = renderDisclaimer();
26+
27+
assert.strictEqual(target.innerText.replace(/\s+/g, ' '), 'Relevant data is sent to Google');
28+
const link = querySelectorErrorOnMissing(target, 'button.link');
29+
assert.strictEqual(link.innerText, 'Relevant data');
30+
assert.strictEqual(link.getAttribute('aria-details'), 'test-tooltip');
31+
});
32+
33+
it('opens the AI settings when "Relevant data" is clicked', () => {
34+
const showView = sinon.stub(UI.ViewManager.ViewManager.instance(), 'showView').resolves();
35+
const target = renderDisclaimer();
36+
37+
querySelectorErrorOnMissing(target, 'button.link').click();
38+
39+
sinon.assert.calledOnceWithExactly(showView, 'chrome-ai');
40+
});
41+
42+
it('shows the disclaimer text and a settings link in the tooltip', () => {
43+
const showView = sinon.stub(UI.ViewManager.ViewManager.instance(), 'showView').resolves();
44+
const target = renderDisclaimer();
45+
46+
const tooltip = querySelectorErrorOnMissing(target, 'devtools-tooltip#test-tooltip');
47+
assert.include(tooltip.textContent, 'Disclaimer text');
48+
const link = querySelectorErrorOnMissing(tooltip, '.tooltip-link');
49+
assert.strictEqual(link.textContent?.trim(), 'Learn about AI in DevTools');
50+
51+
link.click();
52+
sinon.assert.calledOnceWithExactly(showView, 'chrome-ai');
53+
});
54+
});
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2026 The Chromium Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import '../../../ui/components/tooltips/tooltips.js';
6+
7+
import * as i18n from '../../../core/i18n/i18n.js';
8+
import * as UI from '../../../ui/legacy/legacy.js';
9+
import * as Lit from '../../../ui/lit/lit.js';
10+
import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
11+
12+
import relevantDataDisclaimerStyles from './relevantDataDisclaimer.css.js';
13+
14+
const {html} = Lit;
15+
16+
const UIStrings = {
17+
/**
18+
* @description The footer disclaimer that links to more information about the AI feature.
19+
*/
20+
learnAbout: 'Learn about AI in DevTools',
21+
} as const;
22+
23+
const UIStringsNotTranslate = {
24+
/**
25+
* @description Link text at the start of the "Relevant data is sent to Google" disclaimer. Opens the AI settings.
26+
*/
27+
relevantData: 'Relevant data',
28+
/**
29+
* @description Rest of the disclaimer that follows the "Relevant data" link.
30+
*/
31+
isSentToGoogle: 'is sent to Google',
32+
} as const;
33+
34+
const str_ = i18n.i18n.registerUIStrings('panels/ai_assistance/components/RelevantDataDisclaimer.ts', UIStrings);
35+
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
36+
const lockedString = i18n.i18n.lockedString;
37+
38+
function openAiSettings(): void {
39+
void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
40+
}
41+
42+
/**
43+
* Renders "Relevant data is sent to Google". "Relevant data" is a link to the
44+
* AI settings with a rich tooltip that shows `disclaimerText` and a
45+
* "Learn about AI in DevTools" link.
46+
*/
47+
export function renderRelevantDataDisclaimer(tooltipId: string, disclaimerText: string): Lit.LitTemplate {
48+
// clang-format off
49+
return html`
50+
<style>${relevantDataDisclaimerStyles}</style>
51+
<button
52+
class="link"
53+
role="link"
54+
aria-details=${tooltipId}
55+
jslog=${VisualLogging.link('open-ai-settings').track({click: true})}
56+
@click=${(ev: Event) => {
57+
ev.preventDefault();
58+
openAiSettings();
59+
}}
60+
>${lockedString(UIStringsNotTranslate.relevantData)}</button>&nbsp;${lockedString(UIStringsNotTranslate.isSentToGoogle)}<devtools-tooltip
61+
id=${tooltipId}
62+
variant="rich"
63+
><div class="info-tooltip-container">
64+
${disclaimerText}
65+
<button
66+
class="link tooltip-link"
67+
role="link"
68+
jslog=${VisualLogging.link('open-ai-settings').track({click: true})}
69+
@click=${openAiSettings}
70+
>${i18nString(UIStrings.learnAbout)}</button>
71+
</div></devtools-tooltip>`;
72+
// clang-format on
73+
}

‎front_end/panels/ai_assistance/components/chatInput.css‎

Lines changed: 0 additions & 18 deletions

0 commit comments

Comments
 (0)