From 9f3562eb856960e5709d95ea769441da4b6029f9 Mon Sep 17 00:00:00 2001 From: Nikhil Date: Thu, 5 Feb 2026 15:04:08 -0800 Subject: [PATCH] feat: new tab tips section (#304) * fix: tips * fix: show tips only 1/5 times * fix: guard against empty tips array in getRandomTip Co-Authored-By: Claude Opus 4.6 * fix: biome exhaustive deps in SurveyChat voice effect Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- .../entrypoints/app/jtbd-agent/SurveyChat.tsx | 2 +- .../agent/entrypoints/newtab/index/NewTab.tsx | 3 + .../entrypoints/newtab/index/NewTabTip.tsx | 48 ++++++++++ apps/agent/entrypoints/newtab/index/tips.ts | 90 +++++++++++++++++++ apps/agent/lib/constants/analyticsEvents.ts | 3 + 5 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 apps/agent/entrypoints/newtab/index/NewTabTip.tsx create mode 100644 apps/agent/entrypoints/newtab/index/tips.ts diff --git a/apps/agent/entrypoints/app/jtbd-agent/SurveyChat.tsx b/apps/agent/entrypoints/app/jtbd-agent/SurveyChat.tsx index bab46fabb..a701d14f7 100644 --- a/apps/agent/entrypoints/app/jtbd-agent/SurveyChat.tsx +++ b/apps/agent/entrypoints/app/jtbd-agent/SurveyChat.tsx @@ -89,7 +89,7 @@ export const Chat: FC = ({ }) voice.clearTranscript() } - }, [voice.transcript, voice.isTranscribing, voice.clearTranscript]) + }, [voice]) const handleSubmit = (e: FormEvent) => { e.preventDefault() diff --git a/apps/agent/entrypoints/newtab/index/NewTab.tsx b/apps/agent/entrypoints/newtab/index/NewTab.tsx index 07aea6bbe..92de1f8ba 100644 --- a/apps/agent/entrypoints/newtab/index/NewTab.tsx +++ b/apps/agent/entrypoints/newtab/index/NewTab.tsx @@ -53,6 +53,7 @@ import { useSuggestions, } from './lib/suggestions/useSuggestions' import { NewTabBranding } from './NewTabBranding' +import { NewTabTip } from './NewTabTip' import { ScheduleResults } from './ScheduleResults' import { SearchSuggestions } from './SearchSuggestions' import { ShortcutsDialog } from './ShortcutsDialog' @@ -597,6 +598,8 @@ export const NewTab = () => { + {mounted && !isSuggestionsVisible && } + {/* Top sites */} {!isSuggestionsVisible && } diff --git a/apps/agent/entrypoints/newtab/index/NewTabTip.tsx b/apps/agent/entrypoints/newtab/index/NewTabTip.tsx new file mode 100644 index 000000000..afaf71cc2 --- /dev/null +++ b/apps/agent/entrypoints/newtab/index/NewTabTip.tsx @@ -0,0 +1,48 @@ +import { Lightbulb, X } from 'lucide-react' +import { AnimatePresence, motion } from 'motion/react' +import { type FC, useMemo, useState } from 'react' +import { NEWTAB_TIP_DISMISSED_EVENT } from '@/lib/constants/analyticsEvents' +import { track } from '@/lib/metrics/track' +import { dismissTip, getRandomTip, shouldShowTip } from './tips' + +export const NewTabTip: FC = () => { + const tip = useMemo(() => getRandomTip(), []) + const [visible, setVisible] = useState(() => tip !== null && shouldShowTip()) + + const handleDismiss = () => { + setVisible(false) + dismissTip() + if (tip) track(NEWTAB_TIP_DISMISSED_EVENT, { tip_id: tip.id }) + } + + return ( + + {visible && tip && ( + +
+ +

+ + Tip: + {' '} + {tip.text} +

+ +
+
+ )} +
+ ) +} diff --git a/apps/agent/entrypoints/newtab/index/tips.ts b/apps/agent/entrypoints/newtab/index/tips.ts new file mode 100644 index 000000000..a7e5f6e37 --- /dev/null +++ b/apps/agent/entrypoints/newtab/index/tips.ts @@ -0,0 +1,90 @@ +export interface Tip { + id: string + text: string + shortcut?: string +} + +export const TIP_SHOW_PROBABILITY = 0.2 + +const TIP_DISMISSED_KEY = 'tip-dismissed-session' + +export const TIPS: Tip[] = [ + { + id: 'chat-any-page', + text: 'Press Option+K to open the AI Chat panel on any webpage — it includes full page context.', + shortcut: '⌥K', + }, + { + id: 'compare-models', + text: 'Press Cmd+Shift+U to open LLM Hub and query multiple AI models side-by-side.', + shortcut: '⌘⇧U', + }, + { + id: 'switch-models', + text: 'Press Option+L to cycle between AI providers without closing the chat panel.', + shortcut: '⌥L', + }, + { + id: 'screenshot-chat', + text: 'Use the Image button in Chat to capture the visible page and ask visual questions about it.', + }, + { + id: 'copy-page-content', + text: 'Click the Copy button in Chat to grab all webpage text and paste it into your prompt.', + }, + { + id: 'cowork-mode', + text: 'Enable Cowork and select a folder to let the agent browse the web AND create files in a single task.', + }, + { + id: 'scheduled-tasks', + text: 'Set up Scheduled Tasks to run the agent on a timer — results appear right here on your New Tab.', + }, + { + id: 'background-tasks', + text: 'Scheduled tasks run in a hidden window so they never interrupt your browsing.', + }, + { + id: 'claude-code-mcp', + text: 'Connect BrowserOS to Claude Code with the MCP integration for full browser control from your terminal.', + }, + { + id: 'mcp-servers', + text: 'Add MCP servers for Google Calendar, Gmail, Notion, and more to build multi-service workflows.', + }, + { + id: 'import-chrome', + text: 'Go to chrome://settings/importData to import bookmarks, passwords, and history from Chrome in one click.', + }, + { + id: 'ad-blocking', + text: 'BrowserOS comes with uBlock Origin pre-enabled — blocking 10x more ads than Chrome out of the box.', + }, + { + id: 'at-mention-tabs', + text: 'Type @ in the search bar to mention and attach open tabs as context for your AI queries.', + }, + { + id: 'workflows', + text: 'For complex repeatable tasks, build visual Workflows instead of one-off prompts for consistent results.', + }, + { + id: 'model-selection', + text: 'Use Gemini Flash for quick questions and Claude Opus for complex multi-step agent tasks.', + }, +] + +export const shouldShowTip = (): boolean => { + const dismissed = sessionStorage.getItem(TIP_DISMISSED_KEY) + if (dismissed) return false + return Math.random() < TIP_SHOW_PROBABILITY +} + +export const dismissTip = () => { + sessionStorage.setItem(TIP_DISMISSED_KEY, Date.now().toString()) +} + +export const getRandomTip = (): Tip | null => { + if (TIPS.length === 0) return null + return TIPS[Math.floor(Math.random() * TIPS.length)] +} diff --git a/apps/agent/lib/constants/analyticsEvents.ts b/apps/agent/lib/constants/analyticsEvents.ts index 9423c53c7..95f87b2ef 100644 --- a/apps/agent/lib/constants/analyticsEvents.ts +++ b/apps/agent/lib/constants/analyticsEvents.ts @@ -95,6 +95,9 @@ export const NEWTAB_TAB_REMOVED_EVENT = 'newtab.tab.removed' /** @public */ export const NEWTAB_APPS_OPENED_EVENT = 'newtab.apps.opened' +/** @public */ +export const NEWTAB_TIP_DISMISSED_EVENT = 'newtab.tip.dismissed' + /** @public */ export const WORKFLOW_DELETED_EVENT = 'settings.workflow.deleted'