Skip to content

Commit 630d341

Browse files
authored
Merge pull request #157 -- Onboarding flow
Feat/Onboarding flow for BrowserOS
2 parents 4bf78b4 + 0d04f62 commit 630d341

File tree

20 files changed

+1436
-14
lines changed

20 files changed

+1436
-14
lines changed

assets/icons8-google-chrome.svg

Lines changed: 1 addition & 0 deletions
Loading

onboarding.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en" data-theme="light">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>Welcome to BrowserOS</title>
7+
<link rel="icon" type="image/png" href="/assets/browseros.svg" />
8+
<script src="theme-init.js"></script>
9+
</head>
10+
<body>
11+
<div id="root"></div>
12+
<script src="onboarding.js"></script>
13+
</body>
14+
</html>

src/background/handlers/ExecutionHandler.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -289,16 +289,16 @@ export class ExecutionHandler {
289289
message: any,
290290
sendResponse: (response: any) => void
291291
): Promise<void> {
292-
const { tabId, query, metadata } = message
292+
const { tabId, query, chatMode, metadata } = message
293293

294294
Logging.log('ExecutionHandler',
295-
`Received query from newtab for tab ${tabId}: "${query}"`)
295+
`Received query from newtab for tab ${tabId}: "${query}" (mode: ${chatMode ? 'chat' : 'browse'})`)
296296

297297
// Log metrics
298298
Logging.logMetric('query_initiated', {
299299
query,
300300
source: metadata?.source || 'newtab',
301-
mode: 'browse',
301+
mode: chatMode ? 'chat' : 'browse',
302302
executionMode: metadata?.executionMode || 'dynamic',
303303
})
304304

@@ -312,7 +312,8 @@ export class ExecutionHandler {
312312
// Notify sidepanel that execution is starting (for processing state)
313313
chrome.runtime.sendMessage({
314314
type: MessageType.EXECUTION_STARTING,
315-
source: 'newtab'
315+
source: 'newtab',
316+
mode: chatMode ? 'chat' : 'browse'
316317
}).catch(() => {
317318
// Sidepanel might not be ready yet, that's OK - it will pick up state from stream
318319
})
@@ -325,7 +326,7 @@ export class ExecutionHandler {
325326

326327
// Update execution options
327328
this.execution.updateOptions({
328-
mode: 'browse',
329+
mode: chatMode ? 'chat' : 'browse',
329330
tabIds: [tabId],
330331
metadata,
331332
debug: false

src/background/index.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,13 +366,57 @@ async function toggleSidePanel(tabId: number): Promise<void> {
366366
}
367367
}
368368

369+
/**
370+
* Handle extension installation
371+
*/
372+
chrome.runtime.onInstalled.addListener(async (details) => {
373+
console.log('[Onboarding] chrome.runtime.onInstalled fired', { reason: details.reason })
374+
375+
if (details.reason === 'install' || details.reason === 'update') {
376+
Logging.log('Background', `Extension ${details.reason} - checking for first run`)
377+
378+
try {
379+
// Check if onboarding has been completed
380+
const result = await chrome.storage.local.get('hasCompletedOnboarding')
381+
const hasCompletedOnboarding = result.hasCompletedOnboarding
382+
383+
console.log('[Onboarding] Storage check result:', { hasCompletedOnboarding })
384+
385+
// For testing: In development mode with update, always show onboarding to test
386+
const shouldShowOnboarding = !hasCompletedOnboarding ||
387+
(isDevelopmentMode() && details.reason === 'update')
388+
389+
if (shouldShowOnboarding) {
390+
console.log('[Onboarding] Opening onboarding page')
391+
Logging.log('Background', 'Opening onboarding page')
392+
Logging.logMetric('onboarding_started', {
393+
trigger: details.reason,
394+
isDev: isDevelopmentMode()
395+
})
396+
397+
// Open onboarding page in a new tab
398+
const onboardingUrl = chrome.runtime.getURL('onboarding.html')
399+
console.log('[Onboarding] URL:', onboardingUrl)
400+
401+
await chrome.tabs.create({ url: onboardingUrl })
402+
console.log('[Onboarding] Tab created successfully')
403+
} else {
404+
console.log('[Onboarding] Skipping - already completed')
405+
}
406+
} catch (error) {
407+
console.error('[Onboarding] Error during onboarding check:', error)
408+
Logging.log('Background', `Onboarding error: ${error}`, 'error')
409+
}
410+
}
411+
})
412+
369413
/**
370414
* Initialize the extension
371415
*/
372416
function initialize(): void {
373417
Logging.log('Background', 'Nxtscape extension initializing')
374418
Logging.logMetric('extension_initialized')
375-
419+
376420
// Register all handlers
377421
registerHandlers()
378422

src/onboarding/OnboardingApp.tsx

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import React, { useEffect } from 'react'
2+
import { useOnboardingStore } from './stores/onboardingStore'
3+
import { useSettingsStore } from '@/sidepanel/stores/settingsStore'
4+
import { OnboardingLayout } from './components/OnboardingLayout'
5+
import { WelcomeStep } from './components/WelcomeStep'
6+
import { StepOne } from './components/StepOne'
7+
import { StepTwo } from './components/StepTwo'
8+
import { StepThree } from './components/StepThree'
9+
import { VideoStep } from './components/VideoStep'
10+
import { CompletionScreen } from './components/CompletionScreen'
11+
import './styles.css'
12+
13+
export function OnboardingApp() {
14+
const { currentStep } = useOnboardingStore()
15+
const { theme } = useSettingsStore()
16+
17+
// Apply theme on mount and when it changes
18+
useEffect(() => {
19+
const root = document.documentElement
20+
root.classList.remove('dark', 'gray')
21+
if (theme === 'dark') root.classList.add('dark')
22+
if (theme === 'gray') root.classList.add('gray')
23+
}, [theme])
24+
25+
// Render the appropriate step component
26+
const renderStep = () => {
27+
switch (currentStep) {
28+
case 0:
29+
return <WelcomeStep />
30+
case 1:
31+
return <StepOne />
32+
case 2:
33+
return <StepTwo />
34+
case 3:
35+
return <StepThree />
36+
case 4:
37+
return <VideoStep />
38+
case 5:
39+
return <CompletionScreen />
40+
default:
41+
return <WelcomeStep />
42+
}
43+
}
44+
45+
return (
46+
<OnboardingLayout>
47+
<div className="transition-all duration-300 ease-in-out">
48+
{renderStep()}
49+
</div>
50+
</OnboardingLayout>
51+
)
52+
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import React, { useEffect } from 'react'
2+
import { useOnboardingStore } from '../stores/onboardingStore'
3+
4+
export function CompletionScreen() {
5+
const { completeOnboarding } = useOnboardingStore()
6+
7+
// Mark onboarding as completed when this screen is shown
8+
useEffect(() => {
9+
completeOnboarding()
10+
}, [completeOnboarding])
11+
12+
const handleOpenSidePanel = async () => {
13+
try {
14+
// Get the current tab
15+
const [currentTab] = await chrome.tabs.query({ active: true, currentWindow: true })
16+
17+
// Close any open import settings tabs to prevent switching to them
18+
const allTabs = await chrome.tabs.query({ currentWindow: true })
19+
const importSettingsTabs = allTabs.filter(tab =>
20+
tab.url?.includes('chrome://settings/importData') && tab.id !== currentTab?.id
21+
)
22+
23+
// Close import settings tabs
24+
for (const tab of importSettingsTabs) {
25+
if (tab.id) {
26+
await chrome.tabs.remove(tab.id)
27+
}
28+
}
29+
30+
// Open the side panel
31+
if (currentTab?.id) {
32+
await chrome.sidePanel.open({ tabId: currentTab.id })
33+
}
34+
35+
// Redirect to newtab instead of closing the window
36+
setTimeout(() => {
37+
const newtabUrl = chrome.runtime.getURL('newtab.html')
38+
window.location.href = newtabUrl
39+
}, 500)
40+
} catch (error) {
41+
console.error('Failed to open side panel:', error)
42+
// Fallback: redirect to newtab
43+
const newtabUrl = chrome.runtime.getURL('newtab.html')
44+
window.location.href = newtabUrl
45+
}
46+
}
47+
48+
const handleOpenSettings = () => {
49+
chrome.tabs.create({ url: 'chrome://settings/browseros' })
50+
}
51+
52+
return (
53+
<div className="flex flex-col items-center justify-center text-center space-y-8">
54+
{/* Success animation */}
55+
<div className="relative">
56+
<div className="absolute inset-0 bg-brand/20 blur-3xl animate-pulse" />
57+
<img
58+
src="/assets/new_tab_search/browseros.svg"
59+
alt="BrowserOS"
60+
className="relative w-32 h-32 object-contain drop-shadow-2xl"
61+
/>
62+
</div>
63+
64+
{/* Heading */}
65+
<div className="space-y-4">
66+
<h1 className="text-4xl sm:text-5xl font-bold">
67+
You're All Set! 🎉
68+
</h1>
69+
<p className="text-xl text-muted-foreground max-w-2xl mx-auto">
70+
Thank you for downloading BrowserOS! Join our discord or slack community to provide feedback and suggest new features!
71+
</p>
72+
</div>
73+
74+
{/* Actions */}
75+
<div className="flex flex-col sm:flex-row gap-4 pt-4">
76+
<button
77+
onClick={handleOpenSidePanel}
78+
className="px-10 py-4 bg-gradient-to-r from-brand to-orange-500 hover:from-brand/90 hover:to-orange-500/90 text-white font-bold rounded-xl transition-all duration-300 shadow-lg shadow-brand/25 hover:shadow-xl hover:shadow-brand/40 hover:scale-105 active:scale-95"
79+
>
80+
Open AI Agent Panel
81+
</button>
82+
<button
83+
onClick={handleOpenSettings}
84+
className="px-10 py-4 bg-secondary hover:bg-secondary/80 text-secondary-foreground font-bold rounded-xl transition-all duration-300 border-2 border-border hover:scale-105 active:scale-95"
85+
>
86+
Go to Settings
87+
</button>
88+
</div>
89+
90+
{/* Quick links */}
91+
<div className="pt-8 max-w-2xl mx-auto w-full">
92+
<div className="bg-card border border-border rounded-lg p-6 space-y-4">
93+
<h3 className="text-lg font-semibold flex items-center gap-2">
94+
<svg
95+
className="w-5 h-5 text-brand"
96+
fill="none"
97+
stroke="currentColor"
98+
viewBox="0 0 24 24"
99+
>
100+
<path
101+
strokeLinecap="round"
102+
strokeLinejoin="round"
103+
strokeWidth={2}
104+
d="M13.828 10.172a4 4 0 00-5.656 0l-4 4a4 4 0 105.656 5.656l1.102-1.101m-.758-4.899a4 4 0 005.656 0l4-4a4 4 0 00-5.656-5.656l-1.1 1.1"
105+
/>
106+
</svg>
107+
Helpful Resources
108+
</h3>
109+
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
110+
<a
111+
href="https://discord.gg/browseros"
112+
target="_blank"
113+
rel="noopener noreferrer"
114+
className="flex items-center gap-3 p-3 bg-muted/30 hover:bg-muted/50 border border-border hover:border-brand/50 rounded-lg transition-all duration-200 group"
115+
>
116+
<svg
117+
className="w-5 h-5 text-muted-foreground group-hover:text-brand transition-colors"
118+
fill="currentColor"
119+
viewBox="0 0 24 24"
120+
>
121+
<path d="M20.317 4.492c-1.53-.69-3.17-1.2-4.885-1.49a.075.075 0 0 0-.079.036c-.21.369-.444.85-.608 1.23a18.566 18.566 0 0 0-5.487 0 12.36 12.36 0 0 0-.617-1.23A.077.077 0 0 0 8.562 3c-1.714.29-3.354.8-4.885 1.491a.07.07 0 0 0-.032.027C.533 9.093-.32 13.555.099 17.961a.08.08 0 0 0 .031.055 20.03 20.03 0 0 0 5.993 2.98.078.078 0 0 0 .084-.026 13.83 13.83 0 0 0 1.226-1.963.074.074 0 0 0-.041-.104 13.201 13.201 0 0 1-1.872-.878.075.075 0 0 1-.008-.125c.126-.093.252-.19.372-.287a.075.075 0 0 1 .078-.01c3.927 1.764 8.18 1.764 12.061 0a.075.075 0 0 1 .079.009c.12.098.245.195.372.288a.075.075 0 0 1-.006.125c-.598.344-1.22.635-1.873.877a.075.075 0 0 0-.041.105c.36.687.772 1.341 1.225 1.962a.077.077 0 0 0 .084.028 19.963 19.963 0 0 0 6.002-2.981.076.076 0 0 0 .032-.054c.5-5.094-.838-9.52-3.549-13.442a.06.06 0 0 0-.031-.028zM8.02 15.278c-1.182 0-2.157-1.069-2.157-2.38 0-1.312.956-2.38 2.157-2.38 1.21 0 2.176 1.077 2.157 2.38 0 1.312-.956 2.38-2.157 2.38zm7.975 0c-1.183 0-2.157-1.069-2.157-2.38 0-1.312.955-2.38 2.157-2.38 1.21 0 2.176 1.077 2.157 2.38 0 1.312-.946 2.38-2.157 2.38z" />
122+
</svg>
123+
<div className="text-left">
124+
<p className="text-sm font-medium">Discord</p>
125+
<p className="text-xs text-muted-foreground">Join our community</p>
126+
</div>
127+
</a>
128+
129+
<a
130+
href="https://dub.sh/browserOS-slack"
131+
target="_blank"
132+
rel="noopener noreferrer"
133+
className="flex items-center gap-3 p-3 bg-muted/30 hover:bg-muted/50 border border-border hover:border-brand/50 rounded-lg transition-all duration-200 group"
134+
>
135+
<svg
136+
className="w-5 h-5 text-muted-foreground group-hover:text-brand transition-colors"
137+
fill="currentColor"
138+
viewBox="0 0 24 24"
139+
>
140+
<path d="M5.042 15.165a2.528 2.528 0 0 1-2.52 2.523A2.528 2.528 0 0 1 0 15.165a2.527 2.527 0 0 1 2.522-2.52h2.52v2.52zM6.313 15.165a2.527 2.527 0 0 1 2.521-2.52 2.527 2.527 0 0 1 2.521 2.52v6.313A2.528 2.528 0 0 1 8.834 24a2.528 2.528 0 0 1-2.521-2.522v-6.313zM8.834 5.042a2.528 2.528 0 0 1-2.521-2.52A2.528 2.528 0 0 1 8.834 0a2.528 2.528 0 0 1 2.521 2.522v2.52H8.834zM8.834 6.313a2.528 2.528 0 0 1 2.521 2.521 2.528 2.528 0 0 1-2.521 2.521H2.522A2.528 2.528 0 0 1 0 8.834a2.528 2.528 0 0 1 2.522-2.521h6.312zM18.956 8.834a2.528 2.528 0 0 1 2.522-2.521A2.528 2.528 0 0 1 24 8.834a2.528 2.528 0 0 1-2.522 2.521h-2.522V8.834zM17.688 8.834a2.528 2.528 0 0 1-2.523 2.521 2.527 2.527 0 0 1-2.52-2.521V2.522A2.527 2.527 0 0 1 15.165 0a2.528 2.528 0 0 1 2.523 2.522v6.312zM15.165 18.956a2.528 2.528 0 0 1 2.523 2.522A2.528 2.528 0 0 1 15.165 24a2.527 2.527 0 0 1-2.52-2.522v-2.522h2.52zM15.165 17.688a2.527 2.527 0 0 1-2.52-2.523 2.526 2.526 0 0 1 2.52-2.52h6.313A2.527 2.527 0 0 1 24 15.165a2.528 2.528 0 0 1-2.522 2.523h-6.313z" />
141+
</svg>
142+
<div className="text-left">
143+
<p className="text-sm font-medium">Slack</p>
144+
<p className="text-xs text-muted-foreground">Join our workspace</p>
145+
</div>
146+
</a>
147+
148+
<a
149+
href="https://github.com/browseros-ai/BrowserOS"
150+
target="_blank"
151+
rel="noopener noreferrer"
152+
className="flex items-center gap-3 p-3 bg-muted/30 hover:bg-muted/50 border border-border hover:border-brand/50 rounded-lg transition-all duration-200 group"
153+
>
154+
<svg
155+
className="w-5 h-5 text-muted-foreground group-hover:text-brand transition-colors"
156+
fill="currentColor"
157+
viewBox="0 0 24 24"
158+
>
159+
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
160+
</svg>
161+
<div className="text-left">
162+
<p className="text-sm font-medium">GitHub</p>
163+
<p className="text-xs text-muted-foreground">Star our repository</p>
164+
</div>
165+
</a>
166+
167+
<a
168+
href="https://docs.browseros.com"
169+
target="_blank"
170+
rel="noopener noreferrer"
171+
className="flex items-center gap-3 p-3 bg-muted/30 hover:bg-muted/50 border border-border hover:border-brand/50 rounded-lg transition-all duration-200 group"
172+
>
173+
<svg
174+
className="w-5 h-5 text-muted-foreground group-hover:text-brand transition-colors"
175+
fill="none"
176+
stroke="currentColor"
177+
viewBox="0 0 24 24"
178+
>
179+
<path
180+
strokeLinecap="round"
181+
strokeLinejoin="round"
182+
strokeWidth={2}
183+
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
184+
/>
185+
</svg>
186+
<div className="text-left">
187+
<p className="text-sm font-medium">Documentation</p>
188+
<p className="text-xs text-muted-foreground">Learn more</p>
189+
</div>
190+
</a>
191+
</div>
192+
</div>
193+
</div>
194+
</div>
195+
)
196+
}

0 commit comments

Comments
 (0)