From e7cec19db2f88e5fa401d53953bec1159bbd7df8 Mon Sep 17 00:00:00 2001 From: Douglas Daniel Date: Tue, 8 Apr 2025 17:44:07 -0500 Subject: [PATCH] Implement First Time Use UI for Tab Focus (#28363) * Enable Tab Focus UI * Backend change to support first time use UI for tab focus * Fix Illustration Resource * npm run format * Update showFRE related logics in both backend and frontend * FE Review Changes * Update API method name to SetTabFocusEnabled --------- Co-authored-by: Jocelyn Liu --- app/brave_generated_resources.grd | 6 ++ .../resources/tab_search/brave/remove_me.txt | 1 - .../brave/tab_organizer_illustration.svg | 1 + browser/resources/tab_search/sources.gni | 2 +- .../auto_tab_groups/auto_tab_groups_page.css | 48 ++++++++++++ .../auto_tab_groups_page.html.ts | 76 +++++++++++++++---- .../auto_tab_groups/auto_tab_groups_page.ts | 16 ++++ .../tab_search/tab_search_api_proxy.ts | 5 ++ .../ui/webui/tab_search/tab_search.mojom | 2 + .../tab_search/tab_search_page_handler.cc | 5 ++ .../tab_search/tab_search_page_handler.h | 1 + .../ui/webui/tab_search/tab_search_ui.cc | 13 ++++ components/ai_chat/core/common/pref_names.cc | 2 +- 13 files changed, 159 insertions(+), 19 deletions(-) delete mode 100644 browser/resources/tab_search/brave/remove_me.txt create mode 100644 browser/resources/tab_search/brave/tab_organizer_illustration.svg diff --git a/app/brave_generated_resources.grd b/app/brave_generated_resources.grd index 025d66daeda..98c4d7abd39 100644 --- a/app/brave_generated_resources.grd +++ b/app/brave_generated_resources.grd @@ -1408,6 +1408,12 @@ Or change later at $2brave://settings/ext Dismiss + + When opened, this will automatically send the titles and origin of all non-private tabs to Leo to classify them. + + + Enable + diff --git a/browser/resources/tab_search/brave/remove_me.txt b/browser/resources/tab_search/brave/remove_me.txt deleted file mode 100644 index 0c3c67e5bb2..00000000000 --- a/browser/resources/tab_search/brave/remove_me.txt +++ /dev/null @@ -1 +0,0 @@ -Example \ No newline at end of file diff --git a/browser/resources/tab_search/brave/tab_organizer_illustration.svg b/browser/resources/tab_search/brave/tab_organizer_illustration.svg new file mode 100644 index 00000000000..6014ac6192a --- /dev/null +++ b/browser/resources/tab_search/brave/tab_organizer_illustration.svg @@ -0,0 +1 @@ + diff --git a/browser/resources/tab_search/sources.gni b/browser/resources/tab_search/sources.gni index 82cd49dd43f..6d2f13a46a9 100644 --- a/browser/resources/tab_search/sources.gni +++ b/browser/resources/tab_search/sources.gni @@ -7,7 +7,7 @@ brave_tab_search_extras_grdp = "$root_gen_dir/brave/browser/resources/tab_search/brave_extras.grdp" brave_tab_search_static_files = [ - "brave/remove_me.txt", + "brave/tab_organizer_illustration.svg", # SVG and other static files ] diff --git a/chromium_src/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.css b/chromium_src/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.css index 9bfed850530..d3a56273a3b 100644 --- a/chromium_src/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.css +++ b/chromium_src/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.css @@ -148,6 +148,7 @@ } .learn-more-link { + cursor: pointer; color: var(--leo-color-text-tertiary); font-feature-settings: 'ss03' on; text-decoration-line: underline; @@ -166,3 +167,50 @@ padding: var(--leo-spacing-l); box-sizing: border-box; } + +.enable-tab-focus-wrapper { + display: flex; + flex-direction: column; + align-items: stretch; + box-sizing: border-box; + padding: var(--leo-spacing-m); + gap: var(--leo-spacing-m); +} + +.enable-tab-focus-content-wrapper { + display: flex; + flex-direction: column; + align-items: stretch; + box-sizing: border-box; + border-radius: var(--leo-radius-m); + background-color: var(--leo-color-neutral-10); + overflow: hidden; +} + +.enable-tab-focus-illustration { + background-image: url(/brave/tab_organizer_illustration.svg); + background-size: cover; + align-items: stretch; + height: 174px; +} + +.enable-tab-focus-info-wrapper { + display: flex; + flex-direction: column; + padding: var(--leo-spacing-xl); + gap: var(--leo-spacing-m); +} + +.enable-tab-focus-info-text { + font: var(--leo-font-small-regular); + color: var(--leo-color-text-secondary); + text-align: left; +} + +.enable-tab-focus-button-row { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + width: 100%; +} diff --git a/chromium_src/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.html.ts b/chromium_src/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.html.ts index fd1c500b611..c8ef09c5dc3 100644 --- a/chromium_src/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.html.ts +++ b/chromium_src/chrome/browser/resources/tab_search/auto_tab_groups/auto_tab_groups_page.html.ts @@ -62,7 +62,66 @@ function getTopicsHtml(this: AutoTabGroupsPageElement) { `) } +function getHeaderHtml(this: AutoTabGroupsPageElement) { + return html` +
+ ${!this.showBackButton ? html` +
+ + + +
+ ` : ''} +
+
${this.getTitle_()}
+ ${!this.showFRE_ ? html` +
${this.getSubtitle_()}
+ ` : ''} +
+
+ ` +} + +function getEnableTabFocusHtml(this: AutoTabGroupsPageElement) { + return html` +
+ ${getHeaderHtml.bind(this)()} +
+
+
+ + ${this.getPrivacyDisclaimerMessage_()} + +
+ + ${this.getLearnMoreLabel_()} + +
+ + ${this.getEnableButtonLabel_()} + +
+
+
+
+
+ ` +} + export function getHtml(this: AutoTabGroupsPageElement) { + if (this.showFRE_) { + return getEnableTabFocusHtml.bind(this)() + } return html`
-
- ${this.showBackButton ? html` - - - - ` : ''} -
-
${this.getTitle_()}
-
${this.getSubtitle_()}
-
-
+ ${getHeaderHtml.bind(this)()}
Promise<{ windowCreated: boolean, error: Error | null }> undoFocusTabs: () => Promise openLeoGoPremiumPage: () => void + setTabFocusEnabled: () => void } export class BraveTabSearchApiProxyImpl extends TabSearchApiProxyImpl implements BraveTabSearchApiProxy { @@ -30,6 +31,10 @@ export class BraveTabSearchApiProxyImpl extends TabSearchApiProxyImpl implements openLeoGoPremiumPage() { this.handler.openLeoGoPremiumPage() } + + setTabFocusEnabled() { + this.handler.setTabFocusEnabled() + } } TabSearchApiProxyImpl.getInstance = () => { diff --git a/chromium_src/chrome/browser/ui/webui/tab_search/tab_search.mojom b/chromium_src/chrome/browser/ui/webui/tab_search/tab_search.mojom index 50f36e81711..004bc7d1845 100644 --- a/chromium_src/chrome/browser/ui/webui/tab_search/tab_search.mojom +++ b/chromium_src/chrome/browser/ui/webui/tab_search/tab_search.mojom @@ -36,4 +36,6 @@ interface PageHandler { UndoFocusTabs() => (); // Open a new tab for purchasing Leo premium. OpenLeoGoPremiumPage(); + // Set the user pref to enable tab focus with Leo. + SetTabFocusEnabled(); }; diff --git a/chromium_src/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc b/chromium_src/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc index 0c43f75333a..18722311565 100644 --- a/chromium_src/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc +++ b/chromium_src/chrome/browser/ui/webui/tab_search/tab_search_page_handler.cc @@ -246,3 +246,8 @@ void TabSearchPageHandler::OpenLeoGoPremiumPage() { params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; Navigate(¶ms); } + +void TabSearchPageHandler::SetTabFocusEnabled() { + Profile::FromWebUI(web_ui_)->GetPrefs()->SetBoolean( + ai_chat::prefs::kBraveAIChatTabOrganizationEnabled, true); +} diff --git a/chromium_src/chrome/browser/ui/webui/tab_search/tab_search_page_handler.h b/chromium_src/chrome/browser/ui/webui/tab_search/tab_search_page_handler.h index b95fbe8342b..519f5397951 100644 --- a/chromium_src/chrome/browser/ui/webui/tab_search/tab_search_page_handler.h +++ b/chromium_src/chrome/browser/ui/webui/tab_search/tab_search_page_handler.h @@ -59,6 +59,7 @@ class TabSearchPageHandler : public TabSearchPageHandler_ChromiumImpl { GetFocusTabsCallback callback) override; void UndoFocusTabs(UndoFocusTabsCallback callback) override; void OpenLeoGoPremiumPage() override; + void SetTabFocusEnabled() override; void SetOriginalTabsInfoByWindowForTesting( const base::flat_map>& diff --git a/chromium_src/chrome/browser/ui/webui/tab_search/tab_search_ui.cc b/chromium_src/chrome/browser/ui/webui/tab_search/tab_search_ui.cc index a5439ff3e09..e6e14526d66 100644 --- a/chromium_src/chrome/browser/ui/webui/tab_search/tab_search_ui.cc +++ b/chromium_src/chrome/browser/ui/webui/tab_search/tab_search_ui.cc @@ -26,6 +26,11 @@ TabSearchUI::TabSearchUI(content::WebUI* web_ui) profile->GetPrefs()->GetBoolean( ai_chat::prefs::kBraveAIChatTabOrganizationEnabled)); + // Show FRE if user doesn't explicitly enable/disble the feature pref. + update_data.Set("showTabOrganizationFRE", + !profile->GetPrefs()->HasPrefPath( + ai_chat::prefs::kBraveAIChatTabOrganizationEnabled)); + update_data.Set("autoTabGroupsSelectorHeading", l10n_util::GetStringUTF16(IDS_BRAVE_ORGANIZE_TAB_TITLE)); @@ -70,6 +75,14 @@ TabSearchUI::TabSearchUI(content::WebUI* web_ui) "tabOrganizationDismissButtonLabel", l10n_util::GetStringUTF16(IDS_BRAVE_ORGANIZE_TAB_DISMISS_BUTTON_LABEL)); + update_data.Set( + "tabOrganizationPrivacyDisclaimer", + l10n_util::GetStringUTF16(IDS_BRAVE_ORGANIZE_TAB_PRIVACY_DISCLAIMER)); + + update_data.Set( + "tabOrganizationEnableButtonLabel", + l10n_util::GetStringUTF16(IDS_BRAVE_ORGANIZE_TAB_ENABLE_BUTTON_LABEL)); + content::WebUIDataSource::Update(profile, chrome::kChromeUITabSearchHost, std::move(update_data)); } diff --git a/components/ai_chat/core/common/pref_names.cc b/components/ai_chat/core/common/pref_names.cc index 93ef8e1827f..0c8b774ad50 100644 --- a/components/ai_chat/core/common/pref_names.cc +++ b/components/ai_chat/core/common/pref_names.cc @@ -31,7 +31,7 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry) { #endif registry->RegisterBooleanPref(kBraveAIChatContextMenuEnabled, true); registry->RegisterBooleanPref(kBraveAIChatShowToolbarButton, true); - registry->RegisterBooleanPref(kBraveAIChatTabOrganizationEnabled, false); + registry->RegisterBooleanPref(kBraveAIChatTabOrganizationEnabled, true); } registry->RegisterBooleanPref(kEnabledByPolicy, true); }