Skip to content

Commit c0835fa

Browse files
AlinaVarkkiDevtools-frontend LUCI CQ
authored andcommitted
[AI] Show a dialog on generate label button click
Show a dialog when the button is clicked. Will add more tests when I add the logic for it to only appear when the setting is not on and when the input field is empty. Bug: 393063467 Change-Id: I6329d10b929126c3f0f59289adf239c9ae457e6e Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6341555 Auto-Submit: Alina Varkki <[email protected]> Reviewed-by: Jack Franklin <[email protected]> Commit-Queue: Alina Varkki <[email protected]>
1 parent 196851c commit c0835fa

File tree

3 files changed

+114
-4
lines changed

3 files changed

+114
-4
lines changed

front_end/panels/timeline/overlays/OverlaysImpl.test.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import * as Trace from '../../../models/trace/trace.js';
66
import {dispatchClickEvent} from '../../../testing/DOMHelpers.js';
7-
import {describeWithEnvironment} from '../../../testing/EnvironmentHelpers.js';
7+
import {describeWithEnvironment, updateHostConfig} from '../../../testing/EnvironmentHelpers.js';
88
import {
99
makeInstantEvent,
1010
microsecondsTraceWindow,
@@ -14,6 +14,7 @@ import {
1414
import {TraceLoader} from '../../../testing/TraceLoader.js';
1515
import * as RenderCoordinator from '../../../ui/components/render_coordinator/render_coordinator.js';
1616
import * as PerfUI from '../../../ui/legacy/components/perf_ui/perf_ui.js';
17+
import * as PanelCommon from '../../common/common.js';
1718
import * as Timeline from '../timeline.js';
1819

1920
import * as Components from './components/components.js';
@@ -64,7 +65,9 @@ function createCharts(parsedTrace?: Trace.Handlers.Types.ParsedTrace): Overlays.
6465
}
6566

6667
describeWithEnvironment('Overlays', () => {
68+
let showFreDialogStub: sinon.SinonStub<Parameters<typeof PanelCommon.FreDialog.show>, Promise<boolean>>;
6769
beforeEach(() => {
70+
showFreDialogStub = sinon.stub(PanelCommon.FreDialog, 'show');
6871
setupIgnoreListManagerEnvironment();
6972
});
7073

@@ -432,6 +435,54 @@ describeWithEnvironment('Overlays', () => {
432435
});
433436
});
434437

438+
// TODO: update to check if the fre is completed and make the dialog visible dependant on that
439+
it('should show FRE dialog on the ai suggestion button click', async function() {
440+
updateHostConfig({
441+
devToolsAiGeneratedTimelineLabels: {
442+
enabled: true,
443+
}
444+
});
445+
446+
const {parsedTrace} = await TraceLoader.traceEngine(this, 'web-dev.json.gz');
447+
const {overlays, container} = setupChartWithDimensionsAndAnnotationOverlayListeners(parsedTrace);
448+
const charts = createCharts(parsedTrace);
449+
const event = charts.mainProvider.eventByIndex?.(50);
450+
assert.isOk(event);
451+
452+
// Since ENTRY_LABEL is AnnotationOverlay, create it through ModificationsManager
453+
Timeline.ModificationsManager.ModificationsManager.activeManager()?.createAnnotation({
454+
type: 'ENTRY_LABEL',
455+
label: '',
456+
entry: event,
457+
});
458+
459+
await overlays.update();
460+
const overlayDOM = container.querySelector<HTMLElement>('.overlay-type-ENTRY_LABEL');
461+
assert.isOk(overlayDOM);
462+
const component = overlayDOM?.querySelector('devtools-entry-label-overlay');
463+
assert.isOk(component?.shadowRoot);
464+
const elementsWrapper = component.shadowRoot.querySelector<HTMLElement>('.label-parts-wrapper');
465+
assert.isOk(elementsWrapper);
466+
const inputField = elementsWrapper.querySelector<HTMLElement>('.input-field');
467+
assert.isOk(inputField);
468+
469+
// Double click on the label box to make it editable and focus on it
470+
inputField.dispatchEvent(new FocusEvent('dblclick', {bubbles: true}));
471+
472+
const aiLabelButtonWrapper =
473+
elementsWrapper.querySelector<HTMLElement>('.ai-label-button-wrapper') as HTMLSpanElement;
474+
assert.isOk(aiLabelButtonWrapper);
475+
const aiButton = aiLabelButtonWrapper.querySelector<HTMLElement>('.ai-label-button') as HTMLSpanElement;
476+
assert.isOk(aiButton);
477+
478+
// This dialog should not be visible unless the `generate annotation` button is clicked
479+
assert.isFalse(showFreDialogStub.called, 'Expected FreDialog to be not shown but it\'s shown');
480+
aiButton.dispatchEvent(new FocusEvent('click', {bubbles: true}));
481+
482+
// This dialog should be visible
483+
assert.isTrue(showFreDialogStub.called, 'Expected FreDialog to be shown but it\'s not shown');
484+
});
485+
435486
it('toggles overlays container display', async function() {
436487
const {parsedTrace} = await TraceLoader.traceEngine(this, 'web-dev.json.gz');
437488
const {overlays, container} = setupChartWithDimensionsAndAnnotationOverlayListeners(parsedTrace);

front_end/panels/timeline/overlays/components/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ devtools_module("components") {
2727
deps = [
2828
"../../../../core/i18n:bundle",
2929
"../../../../models/trace:bundle",
30+
"../../../../panels/common:bundle",
3031
"../../../../ui/components/helpers:bundle",
3132
"../../../../ui/components/icon_button:bundle",
3233
"../../../../ui/lit:bundle",

front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ import '../../../../ui/components/icon_button/icon_button.js';
66

77
import * as i18n from '../../../../core/i18n/i18n.js';
88
import * as Platform from '../../../../core/platform/platform.js';
9+
import * as Root from '../../../../core/root/root.js';
910
import * as ComponentHelpers from '../../../../ui/components/helpers/helpers.js';
11+
import * as UI from '../../../../ui/legacy/legacy.js';
1012
import * as ThemeSupport from '../../../../ui/legacy/theme_support/theme_support.js';
1113
import * as Lit from '../../../../ui/lit/lit.js';
1214
import * as VisualLogging from '../../../../ui/visual_logging/visual_logging.js';
15+
import * as PanelCommon from '../../../common/common.js';
1316

1417
import stylesRaw from './entryLabelOverlay.css.js';
1518

@@ -43,7 +46,26 @@ const UIStringsNotTranslate = {
4346
*/
4447
generateLabelSecurityDisclaimer:
4548
'The selected call stack is sent to Google. The content you submit and that is generated by this feature will be used to improve Google’s AI models. This is an experimental AI feature and won’t always get it right.',
46-
49+
/**
50+
*@description Header text for the AI-powered annotations suggestions disclaimer dialog.
51+
*/
52+
freDisclaimerHeader: 'Get AI-powered annotation suggestions',
53+
/**
54+
*@description First disclaimer item text for the fre dialog - AI won't always get it right.
55+
*/
56+
freDisclaimerAiWontAlwaysGetItRight: 'This feature uses AI and won’t always get it right',
57+
/**
58+
*@description Second disclaimer item text for the fre dialog - trace data is sent to Google.
59+
*/
60+
freDisclaimerPrivacyDataSentToGoogle: 'Performance trace is sent to Google to generate annotation suggestions',
61+
/**
62+
*@description Third disclaimer item text part for the fre dialog part - you can control this setting from the settings panel (because 'settings panel' part of the string is a link, it is attached separately).
63+
*/
64+
freDisclaimerControlSettingFrom: 'You can control this feature in the',
65+
/**
66+
*@description Third disclaimer item text part for the fre dialog part - settings panel text.
67+
*/
68+
settingsPanel: 'settings panel',
4769
} as const;
4870

4971
const str_ = i18n.i18n.registerUIStrings('panels/timeline/overlays/components/EntryLabelOverlay.ts', UIStrings);
@@ -357,6 +379,40 @@ export class EntryLabelOverlay extends HTMLElement {
357379
}
358380
}
359381

382+
async #handleAiButtonClick(): Promise<void> {
383+
await PanelCommon.FreDialog.show({
384+
header: {iconName: 'pen-spark', text: lockedString(UIStringsNotTranslate.freDisclaimerHeader)},
385+
reminderItems: [
386+
{
387+
iconName: 'psychiatry',
388+
content: lockedString(UIStringsNotTranslate.freDisclaimerAiWontAlwaysGetItRight),
389+
},
390+
{
391+
iconName: 'google',
392+
content: lockedString(UIStringsNotTranslate.freDisclaimerPrivacyDataSentToGoogle),
393+
},
394+
{
395+
iconName: 'gear',
396+
// clang-format off
397+
content: html`
398+
${lockedString(UIStringsNotTranslate.freDisclaimerControlSettingFrom)}
399+
<x-link
400+
@click=${() => {
401+
void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
402+
}}
403+
class="link"
404+
jslog=${VisualLogging.link('open-ai-settings').track({
405+
click: true
406+
})}
407+
>${lockedString(UIStringsNotTranslate.settingsPanel)}</x-link>`,
408+
// clang-format on
409+
}
410+
],
411+
// TODO: Update this href to be the correct link.
412+
learnMoreHref: Platform.DevToolsPath.EmptyUrlString
413+
});
414+
}
415+
360416
#renderAiButton(): Lit.TemplateResult {
361417
// clang-format off
362418
return html`
@@ -367,7 +423,7 @@ export class EntryLabelOverlay extends HTMLElement {
367423
@mousedown=${(e: Event) => e.preventDefault()}>
368424
<button
369425
class="ai-label-button"
370-
@click=${(): void => {}}>
426+
@click=${this.#handleAiButtonClick}>
371427
<devtools-icon
372428
class="pen-icon"
373429
.name=${'pen-spark'}
@@ -406,7 +462,9 @@ export class EntryLabelOverlay extends HTMLElement {
406462
contenteditable=${this.#isLabelEditable ? 'plaintext-only' : false}
407463
jslog=${VisualLogging.textField('timeline.annotations.entry-label-input').track({keydown: true, click: true})}
408464
></span>
409-
<!-- ${this.#renderAiButton()} -->
465+
466+
${Root.Runtime.hostConfig.devToolsAiGeneratedTimelineLabels?.enabled ?
467+
this.#renderAiButton():Lit.nothing}
410468
</span>
411469
<svg class="connectorContainer">
412470
<line/>

0 commit comments

Comments
 (0)