UNPKG

chrome-devtools-frontend

Version:
195 lines (168 loc) • 6.39 kB
// Copyright 2026 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // This dialog will be shown during M146 and removed by M147. // // To enable: // --enable-features=DevToolsGeminiRebranding import '../../ui/components/switch/switch.js'; import '../../ui/kit/kit.js'; import * as Common from '../../core/common/common.js'; import * as Host from '../../core/host/host.js'; import * as i18n from '../../core/i18n/i18n.js'; import * as Root from '../../core/root/root.js'; import * as Geometry from '../../models/geometry/geometry.js'; import * as Buttons from '../../ui/components/buttons/buttons.js'; import * as UI from '../../ui/legacy/legacy.js'; import {html, render} from '../../ui/lit/lit.js'; import * as VisualLogging from '../../ui/visual_logging/visual_logging.js'; import styles from './geminiRebrandPromoDialog.css.js'; const UIStrings = { /** * @description Aria label for the dialog */ dialogAriaLabel: 'Gemini 3 Flash in DevTools', /** * * @description Button text for dismissing the dialog. */ dismiss: 'Dismiss', /** * @description Button text for getting started. */ getStarted: 'Get started', /** * @description Detail message shown in the dialog. */ detailAiCompanion: 'Meet your AI-powered companion for web dev', /** * @description Detail message shown in the dialog. */ detailConsoleErrors: 'Get instant, accurate answers for console errors', /** * @description Detail message shown in the dialog. */ detailGenerateCode: 'Generate CSS and JS snippets on the fly', /** * @description Detail message shown in the dialog. */ detailPerformance: 'Automatically find issues in performance traces', } as const; const str_ = i18n.i18n.registerUIStrings('panels/common/GeminiRebrandPromoDialog.ts', UIStrings); const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); interface ViewInput { onGetStartedClick: () => void; onCancelClick: () => void; } type View = (input: ViewInput, output: undefined, target: HTMLElement) => void; const PROMO_IMAGE_1X = new URL('../../Images/geminiInDevTools.png', import.meta.url).toString(); const PROMO_IMAGE_2X = new URL('../../Images/geminiInDevTools_2x.png', import.meta.url).toString(); export const DEFAULT_VIEW: View = (input, _output, target): void => { // clang-format off render( html` <style>${styles}</style> <div class="header"> <div class="title">Gemini 3 Flash in DevTools</div> <div class="close-button"> <devtools-button aria-hidden="true" tabindex="-1" .iconName=${'cross'} .variant=${Buttons.Button.Variant.ICON} .size=${Buttons.Button.Size.REGULAR} .title=${i18nString(UIStrings.dismiss)} jslog=${VisualLogging.close().track({click: true}).context('gemini-promo-dismiss')} @click=${() => input.onCancelClick()} ></devtools-button> </div> </div> <img class="banner-image" srcset=${`${PROMO_IMAGE_1X} 1x, ${PROMO_IMAGE_2X} 2x`}> <div class="main-content"> <div class="detail-row"> <devtools-icon name="performance"></devtools-icon> <div>${i18nString(UIStrings.detailAiCompanion)}</div> </div> <div class="detail-row"> <devtools-icon name="lightbulb-spark"></devtools-icon> <div>${i18nString(UIStrings.detailConsoleErrors)}</div> </div> <div class="detail-row"> <devtools-icon name="text-analysis"></devtools-icon> <div>${i18nString(UIStrings.detailGenerateCode)}</div> </div> <div class="detail-row"> <devtools-icon name="smart-assistant"></devtools-icon> <div>${i18nString(UIStrings.detailPerformance)}</div> </div> </div> <div class="buttons"> <devtools-button .variant=${Buttons.Button.Variant.OUTLINED} jslog=${VisualLogging.close().track({click: true}).context('gemini-promo-dismiss')} @click=${input.onCancelClick}>${i18nString(UIStrings.dismiss)}</devtools-button> <devtools-button .variant=${Buttons.Button.Variant.PRIMARY} .jslogContext=${'gemini-promo-get-started'} @click=${input.onGetStartedClick}>${i18nString(UIStrings.getStarted)}</devtools-button> </div> `, target ); // clang-format on }; export class GeminiRebrandPromoDialog extends UI.Widget.VBox { #view: View; #dialog: UI.Dialog.Dialog; constructor( options: { dialog: UI.Dialog.Dialog, onSuccess?: () => void, onCancel?: () => void, }, view?: View) { super(); this.#dialog = options.dialog; this.#view = view ?? DEFAULT_VIEW; this.requestUpdate(); } async #onGetStartedClick(): Promise<void> { this.#dialog.hide(); await UI.ViewManager.ViewManager.instance().showView('freestyler'); } #onCancelClick(): void { this.#dialog.hide(); } override performUpdate(): void { const viewInput: ViewInput = { onGetStartedClick: this.#onGetStartedClick.bind(this), onCancelClick: this.#onCancelClick.bind(this), }; this.#view(viewInput, undefined, this.contentElement); } static show(): void { const dialog = new UI.Dialog.Dialog('gemini-promo-dialog'); dialog.setAriaLabel(i18nString(UIStrings.dialogAriaLabel)); dialog.setMaxContentSize(new Geometry.Size(384, 500)); dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.SET_EXACT_WIDTH_MAX_HEIGHT); dialog.setDimmed(true); new GeminiRebrandPromoDialog({dialog}).show(dialog.contentElement); dialog.show(undefined, /* stack */ true); } static async maybeShow(): Promise<void> { if (!Root.Runtime.hostConfig.aidaAvailability?.enabled) { return; } const currentAidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions(); if (currentAidaAvailability !== Host.AidaClient.AidaAccessPreconditions.AVAILABLE) { return; } const setting = Common.Settings.Settings.instance().createSetting<boolean>( 'gemini-promo-dialog-shown', false, Common.Settings.SettingStorageType.SYNCED); if (setting.get()) { return; } setting.set(true); GeminiRebrandPromoDialog.show(); } }