Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
20c369b
Enhanced onboarding page visual design
spencrmartin Aug 18, 2025
1e451e9
Pr 4156 (#4162)
spencrmartin Aug 18, 2025
8a922cb
Enhance shimmer effect to be more subtle and continuous
spencrmartin Aug 18, 2025
dd10b0c
Restore proper icon positioning above card headings
spencrmartin Aug 18, 2025
7a8e011
Fix JSX syntax error and clean up card structure
spencrmartin Aug 18, 2025
56fe403
Fix CSS syntax error - remove orphaned keyframe rules
spencrmartin Aug 18, 2025
3aab3bd
Standardize provider settings header with consistent back button
spencrmartin Aug 18, 2025
005a89b
Fix back button spacing to prevent macOS stoplight overlap
spencrmartin Aug 18, 2025
a150b18
Update onboarding header text from 'Configure your providers' to 'Oth…
spencrmartin Aug 18, 2025
7f0d078
Center-align provider cards while keeping header text left-aligned
spencrmartin Aug 18, 2025
153db2a
Align OllamaSetup component with onboarding card design pattern
spencrmartin Aug 18, 2025
63bd766
Increase icon-to-header spacing to 48px on welcome page
spencrmartin Aug 18, 2025
795e4ec
Replace Ollama status box with detected pill badge for consistency
spencrmartin Aug 18, 2025
0c39f48
Add 64px spacing between detected pill and button
spencrmartin Aug 18, 2025
04f62c4
Convert 'not detected' message to pill badge on Ollama setup page
spencrmartin Aug 18, 2025
bbab2a4
Simplify cancel button text from 'Use a different provider' to 'Cancel'
spencrmartin Aug 18, 2025
603672d
Fix navigation bugs after provider selection (#4204)
spencrmartin Aug 19, 2025
ab9eb87
fix tests
zanesq Aug 19, 2025
ae24f9e
revert accidental changes
zanesq Aug 19, 2025
f334328
Add scrollable layout to onboarding page (#4206)
spencrmartin Aug 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions ui/desktop/src/components/OllamaSetup.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ describe('OllamaSetup', () => {
render(<OllamaSetup onSuccess={mockOnSuccess} onCancel={mockOnCancel} />);

await waitFor(() => {
fireEvent.click(screen.getByText('Use a different provider'));
fireEvent.click(screen.getByText('Cancel'));
});

expect(mockOnCancel).toHaveBeenCalled();
Expand Down Expand Up @@ -156,7 +156,7 @@ describe('OllamaSetup', () => {
render(<OllamaSetup onSuccess={mockOnSuccess} onCancel={mockOnCancel} />);

await waitFor(() => {
expect(screen.getByText(/Ollama is running on your system/)).toBeInTheDocument();
expect(screen.getByText(/Ollama is detected and running/)).toBeInTheDocument();
});
});

Expand Down Expand Up @@ -252,7 +252,7 @@ describe('OllamaSetup', () => {
pollCallback!({ isRunning: true, host: 'http://127.0.0.1:11434' });

await waitFor(() => {
expect(screen.getByText('Ollama is running on your system')).toBeInTheDocument();
expect(screen.getByText('Ollama is detected and running')).toBeInTheDocument();
});
});
});
Expand Down
31 changes: 17 additions & 14 deletions ui/desktop/src/components/OllamaSetup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '../utils/ollamaDetection';
import { initializeSystem } from '../utils/providerUtils';
import { toastService } from '../toasts';
import { Ollama } from './icons';

interface OllamaSetupProps {
onSuccess: () => void;
Expand Down Expand Up @@ -144,7 +145,9 @@ export function OllamaSetup({ onSuccess, onCancel }: OllamaSetupProps) {

return (
<div className="space-y-6">
<div className="text-center">
{/* Header with icon above heading - left aligned like onboarding cards */}
<div className="text-left">
<Ollama className="w-6 h-6 mb-3 text-text-standard" />
<h3 className="text-lg font-semibold text-text-standard mb-2">Ollama Setup</h3>
<p className="text-text-muted">
Ollama lets you run AI models for free, private and locally on your computer.
Expand All @@ -153,8 +156,8 @@ export function OllamaSetup({ onSuccess, onCancel }: OllamaSetupProps) {

{ollamaDetected ? (
<div className="space-y-4">
<div className="bg-background-success/10 border border-border-success rounded-lg p-4">
<p className="text-text-success text-center">✓ Ollama is running on your system</p>
<div className="flex items-start mb-16">
<span className="inline-block px-2 py-1 text-xs font-medium bg-green-600 text-white rounded-full">Ollama is detected and running</span>
</div>

{modelStatus === 'checking' ? (
Expand All @@ -163,11 +166,11 @@ export function OllamaSetup({ onSuccess, onCancel }: OllamaSetupProps) {
</div>
) : modelStatus === 'not-available' ? (
<div className="space-y-4">
<div className="bg-background-warning/10 border border-border-warning rounded-lg p-4">
<p className="text-text-warning text-center text-sm">
<div className="flex items-start mb-16">
<p className="text-text-warning text-sm">
The {getPreferredModel()} model is not installed
</p>
<p className="text-text-muted text-center text-xs mt-1">
<p className="text-text-muted text-xs mt-1">
This model is recommended for the best experience with Goose
</p>
</div>
Expand All @@ -182,12 +185,12 @@ export function OllamaSetup({ onSuccess, onCancel }: OllamaSetupProps) {
) : modelStatus === 'downloading' ? (
<div className="space-y-4">
<div className="bg-background-info/10 border border-border-info rounded-lg p-4">
<p className="text-text-info text-center text-sm">
<p className="text-text-info text-sm">
Downloading {getPreferredModel()}...
</p>
{downloadProgress && (
<>
<p className="text-text-muted text-center text-xs mt-2">
<p className="text-text-muted text-xs mt-2">
{downloadProgress.status}
</p>
{downloadProgress.total && downloadProgress.completed && (
Expand All @@ -200,7 +203,7 @@ export function OllamaSetup({ onSuccess, onCancel }: OllamaSetupProps) {
}}
/>
</div>
<p className="text-text-muted text-center text-xs mt-1">
<p className="text-text-muted text-xs mt-1">
{Math.round((downloadProgress.completed / downloadProgress.total) * 100)}%
</p>
</div>
Expand All @@ -221,17 +224,17 @@ export function OllamaSetup({ onSuccess, onCancel }: OllamaSetupProps) {
</div>
) : (
<div className="space-y-4">
<div className="bg-background-warning/10 border border-border-warning rounded-lg p-4">
<p className="text-text-warning text-center">Ollama is not detected on your system</p>
<div className="flex items-start mb-16">
<span className="inline-block px-2 py-1 text-xs font-medium bg-orange-600 text-white rounded-full">Ollama is not detected on your system</span>
</div>

{isPolling ? (
<div className="space-y-4">
<div className="flex items-center justify-center py-4">
<div className="animate-spin rounded-full h-6 w-6 border-t-2 border-b-2 border-textStandard"></div>
</div>
<p className="text-center text-text-muted text-sm">Waiting for Ollama to start...</p>
<p className="text-center text-text-muted text-xs">
<p className="text-text-muted text-sm">Waiting for Ollama to start...</p>
<p className="text-text-muted text-xs">
Once Ollama is installed and running, we'll automatically detect it.
</p>
</div>
Expand All @@ -253,7 +256,7 @@ export function OllamaSetup({ onSuccess, onCancel }: OllamaSetupProps) {
onClick={onCancel}
className="w-full px-6 py-3 bg-transparent text-text-muted rounded-lg hover:bg-background-muted transition-colors"
>
Use a different provider
Cancel
</button>
</div>
);
Expand Down
169 changes: 124 additions & 45 deletions ui/desktop/src/components/ProviderGuard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { initializeSystem } from '../utils/providerUtils';
import { toastService } from '../toasts';
import { OllamaSetup } from './OllamaSetup';
import { checkOllamaStatus } from '../utils/ollamaDetection';
import { Goose } from './icons/Goose';
import { OpenRouter, Ollama } from './icons';

interface ProviderGuardProps {
children: React.ReactNode;
Expand Down Expand Up @@ -73,6 +75,9 @@ export default function ProviderGuard({ children }: ProviderGuardProps) {
setOpenRouterSetupState(null);
setShowFirstTimeSetup(false);
setHasProvider(true);

// Navigate to chat after successful setup
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think will need to check this running packaged, I had a hell of a time with things like this vs just run-ui

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested with the package also and it's working. I think its not so much a concern anymore with the new ui routing system.

navigate('/', { replace: true });
} else {
throw new Error('Provider or model not found after OpenRouter setup');
}
Expand Down Expand Up @@ -106,8 +111,6 @@ export default function ProviderGuard({ children }: ProviderGuardProps) {
const model = (await read('GOOSE_MODEL', false)) ?? config.GOOSE_DEFAULT_MODEL;

// Always check for Ollama regardless of provider status
const ollamaStatus = await checkOllamaStatus();
setOllamaDetected(ollamaStatus.isRunning);

if (provider && model) {
console.log('ProviderGuard - Provider and model found, continuing normally');
Expand All @@ -116,6 +119,9 @@ export default function ProviderGuard({ children }: ProviderGuardProps) {
console.log('ProviderGuard - No provider/model configured');
setShowFirstTimeSetup(true);
}
const ollamaStatus = await checkOllamaStatus();
setOllamaDetected(ollamaStatus.isRunning);

} catch (error) {
// On error, assume no provider and redirect to welcome
console.error('Error checking provider configuration:', error);
Expand Down Expand Up @@ -170,7 +176,7 @@ export default function ProviderGuard({ children }: ProviderGuardProps) {

if (showOllamaSetup) {
return (
<div className="h-screen w-full flex flex-col items-center justify-center bg-background-default">
<div className="min-h-screen w-full flex flex-col items-center justify-center p-4 bg-background-default">
<div className="max-w-md w-full mx-auto p-8">
<div className="mb-8 text-center">
<WelcomeGooseLogo />
Expand All @@ -179,6 +185,8 @@ export default function ProviderGuard({ children }: ProviderGuardProps) {
onSuccess={() => {
setShowOllamaSetup(false);
setHasProvider(true);
// Navigate to chat after successful setup
navigate('/', { replace: true });
}}
onCancel={() => {
setShowOllamaSetup(false);
Expand All @@ -192,53 +200,124 @@ export default function ProviderGuard({ children }: ProviderGuardProps) {

if (showFirstTimeSetup) {
return (
<div className="h-screen w-full flex flex-col items-center justify-center bg-background-default">
<div className="max-w-md w-full mx-auto p-8 text-center">
<WelcomeGooseLogo />
<h1 className="text-2xl font-bold text-text-standard mt-8 mb-4">Welcome to Goose!</h1>
<p className="text-text-muted mb-8">
Let's get you set up with an AI provider to start using Goose.
</p>

<div className="space-y-4">
<button
onClick={handleOpenRouterSetup}
className="w-full px-6 py-3 bg-background-muted text-text-standard rounded-lg hover:bg-background-hover transition-colors font-medium flex items-center justify-center gap-2"
>
Automatic setup with OpenRouter (recommended)
</button>

<button
onClick={() => {
setShowFirstTimeSetup(false);
setShowOllamaSetup(true);
}}
className="w-full px-6 py-3 bg-background-muted text-text-standard rounded-lg hover:bg-background-hover transition-colors font-medium flex items-center justify-center gap-2"
>
{ollamaDetected ? (
<>
<span className="text-text-success">●</span>
Use Ollama (auto detected)
</>
) : (
'Set up Ollama (run AI locally and free)'
<div className="h-screen w-full bg-background-default overflow-hidden">
<div className="h-full overflow-y-auto">
<div className="min-h-full flex flex-col items-center justify-center p-4 py-8">
<div className="max-w-lg w-full mx-auto p-8">
{/* Header section - same width as buttons, left aligned */}
<div className="text-left mb-8 sm:mb-12">
<div className="space-y-3 sm:space-y-4">
<div className="origin-bottom-left goose-icon-animation">
<Goose className="size-6 sm:size-8" />
</div>
<h1 className="text-2xl sm:text-4xl font-light text-left">
Welcome to Goose
</h1>
</div>
<p className="text-text-muted text-base sm:text-lg mt-4 sm:mt-6">
Since it's your first time here, let's get your set you with a provider so we can make incredible work together.
</p>
</div>

{/* Setup options - same width container */}
<div className="space-y-3 sm:space-y-4">
{/* Primary OpenRouter Card with subtle shimmer - wrapped for badge positioning */}
<div className="relative">
{/* Recommended badge - positioned relative to wrapper */}
<div className="absolute -top-2 -right-2 sm:-top-3 sm:-right-3 z-20">
<span className="inline-block px-2 py-1 text-xs font-medium bg-blue-600 text-white rounded-full">
Recommended
</span>
</div>

<div
onClick={handleOpenRouterSetup}
className="relative w-full p-4 sm:p-6 bg-background-muted border border-background-hover rounded-xl hover:border-text-muted transition-all duration-200 cursor-pointer group overflow-hidden"
>
{/* Subtle shimmer effect */}
<div className="absolute inset-0 -translate-x-full animate-shimmer bg-gradient-to-r from-transparent via-white/8 to-transparent"></div>

<div className="relative flex items-start justify-between mb-3">
<div className="flex-1">
<OpenRouter className="w-5 h-5 sm:w-6 sm:h-6 mb-12 text-text-standard" />
<h3 className="font-medium text-text-standard text-sm sm:text-base">
Automatic setup with OpenRouter
</h3>
</div>
<div className="text-text-muted group-hover:text-text-standard transition-colors">
<svg className="w-4 h-4 sm:w-5 sm:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</div>
</div>
<p className="relative text-text-muted text-sm sm:text-base">
Get instant access to multiple AI models including GPT-4, Claude, and more. Quick setup with just a few clicks.
</p>
</div>
</div>

{/* Ollama Card - outline style */}
<div className="relative">
{/* Detected badge - similar to recommended but green */}
{ollamaDetected && (
<div className="absolute -top-2 -right-2 sm:-top-3 sm:-right-3 z-20">
<span className="inline-block px-2 py-1 text-xs font-medium bg-green-600 text-white rounded-full">
Detected
</span>
</div>
)}
</button>

<div
onClick={() => {
setShowFirstTimeSetup(false);
setShowOllamaSetup(true);
}}
className="w-full p-4 sm:p-6 bg-transparent border border-background-hover rounded-xl hover:border-text-muted transition-all duration-200 cursor-pointer group"
>
<div className="flex items-start justify-between mb-3">
<div className="flex-1">
<Ollama className="w-5 h-5 sm:w-6 sm:h-6 mb-12 text-text-standard" />
<h3 className="font-medium text-text-standard text-sm sm:text-base">
Ollama
</h3>
</div>
<div className="text-text-muted group-hover:text-text-standard transition-colors flex-shrink-0">
<svg className="w-4 h-4 sm:w-5 sm:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</div>
</div>
<p className="text-text-muted text-sm sm:text-base">
Run AI models locally on your computer. Completely free and private with no internet required.
</p>
</div>
</div>

<button
{/* Other providers Card - outline style */}
<div
onClick={() => navigate('/welcome', { replace: true })}
className="w-full px-6 py-3 bg-background-muted text-text-standard rounded-lg hover:bg-background-hover transition-colors font-medium"
className="w-full p-4 sm:p-6 bg-transparent border border-background-hover rounded-xl hover:border-text-muted transition-all duration-200 cursor-pointer group"
>
Configure Other Providers (advanced)
</button>
</div>
<div className="flex items-start justify-between mb-3">
<div className="flex-1">
<h3 className="font-medium text-text-standard text-sm sm:text-base">
Other providers
</h3>
</div>
<div className="text-text-muted group-hover:text-text-standard transition-colors">
<svg className="w-4 h-4 sm:w-5 sm:h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
</svg>
</div>
</div>
<p className="text-text-muted text-sm sm:text-base">
If you've already signed up for providers like Anthropic, OpenAI etc, you can enter your own keys.
</p>
</div>

<p className="text-sm text-text-muted mt-6">
OpenRouter provides instant access to multiple AI models with a simple setup.
{ollamaDetected
? ' Ollama is also detected on your system for running models locally.'
: ' You can also install Ollama to run free AI models locally on your computer.'}
</p>
</div>
</div>
</div>
</div>
</div>
);
Expand Down
45 changes: 45 additions & 0 deletions ui/desktop/src/components/icons/Ollama.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
export default function Ollama({ className = '' }) {
return (
<svg
width="69"
height="88"
viewBox="0 0 69 88"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
className={className}
>
<path
d="M65 85L62.0637 70.8077C62.0222 70.6074 62.0432 70.3992 62.1238 70.2112L64.3359 65.0497C65.7194 61.8214 65.7378 58.1708 64.387 54.9287L62.4897 50.3753C61.8577 48.8586 61.8754 47.149 62.5386 45.6457L64.1296 42.0397C66.8761 35.8141 64.3334 28.5656 58.0958 25.8464C55.8537 24.869 53.319 23.8076 50.5427 22.7131C50.1848 22.572 49.843 22.3777 49.5389 22.1421C46.6697 19.9189 44.1695 18.0613 42.1059 16.5691C38.1157 13.6838 32.8534 13.645 28.9759 16.68C27.1886 18.0789 25.0635 19.8198 22.638 21.9398C22.2177 22.3071 21.7135 22.5905 21.1813 22.7586C18.6462 23.5593 16.4068 24.2677 14.4298 24.8938C6.83415 27.2996 3.28234 35.8919 6.92194 42.9796L8.28723 45.6383C9.05963 47.1424 9.16151 48.9027 8.5678 50.4859L6.65444 55.5882C5.5855 58.4387 5.55086 61.5739 6.55656 64.4473L8.42485 69.7853C8.47432 69.9266 8.49142 70.0773 8.47488 70.2261L7 83.5"
stroke="currentColor"
strokeWidth="5"
strokeLinecap="round"
/>
<circle cx="18.5" cy="40.5" r="3" fill="currentColor" />
<circle cx="52.5" cy="40.5" r="3" fill="currentColor" />
<path
d="M35.5 38C42.1664 38 47 42.3128 47 47C47 51.6872 42.1664 56 35.5 56C28.8336 56 24 51.6872 24 47C24 42.3128 28.8336 38 35.5 38Z"
stroke="currentColor"
strokeWidth="3"
/>
<path
d="M33.5 45L35.5 47M35.5 47L37.5 45M35.5 47V50"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
/>
<path
d="M25 20L23.8142 14.0708C23.2738 11.369 22.3756 8.75127 21.1434 6.28682L19.6522 3.30442C18.9533 1.90667 16.9727 1.86785 16.2196 3.23714V3.23714C13.7795 7.67371 12.5 12.6549 12.5 17.7183V26"
stroke="currentColor"
strokeWidth="4"
strokeLinecap="round"
/>
<path
d="M46.5 20.5L47.7083 14.6911C48.2867 11.9102 49.2445 9.22205 50.5549 6.70202L52.1438 3.64663C52.8504 2.28778 54.7805 2.24912 55.5409 3.57858V3.57858C58.1353 8.11412 59.5 13.2486 59.5 18.4737V26.5"
stroke="currentColor"
strokeWidth="4"
strokeLinecap="round"
/>
</svg>
);
}
Loading
Loading