Enhanced onboarding page visual design (#4156)
Co-authored-by: Zane Staggs <zane@squareup.com>
This commit is contained in:
@@ -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();
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
@@ -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' ? (
|
||||
@@ -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>
|
||||
@@ -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 && (
|
||||
@@ -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>
|
||||
@@ -221,8 +224,8 @@ 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 ? (
|
||||
@@ -230,8 +233,8 @@ export function OllamaSetup({ onSuccess, onCancel }: OllamaSetupProps) {
|
||||
<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>
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
@@ -73,6 +75,9 @@ export default function ProviderGuard({ children }: ProviderGuardProps) {
|
||||
setOpenRouterSetupState(null);
|
||||
setShowFirstTimeSetup(false);
|
||||
setHasProvider(true);
|
||||
|
||||
// Navigate to chat after successful setup
|
||||
navigate('/', { replace: true });
|
||||
} else {
|
||||
throw new Error('Provider or model not found after OpenRouter setup');
|
||||
}
|
||||
@@ -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');
|
||||
@@ -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);
|
||||
@@ -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 />
|
||||
@@ -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);
|
||||
@@ -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="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>
|
||||
|
||||
<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>
|
||||
{/* 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>
|
||||
|
||||
<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)'
|
||||
{/* 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>
|
||||
);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
export default function OpenRouter({ className = '' }) {
|
||||
return (
|
||||
<svg
|
||||
width="202"
|
||||
height="171"
|
||||
viewBox="0 0 202 171"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-hidden="true"
|
||||
className={className}
|
||||
>
|
||||
<path
|
||||
d="M2.20001 85.5004C8.10001 85.5004 30.8 80.4004 42.5 73.7004C54.3 67.1004 54.3 67.1004 78.6 49.8004C109.4 28.0004 131.1 35.3004 166.8 35.3004"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M2.20001 85.5004C8.10001 85.5004 30.8 80.4004 42.5 73.7004C54.3 67.1004 54.3 67.1004 78.6 49.8004C109.4 28.0004 131.1 35.3004 166.8 35.3004"
|
||||
stroke="currentColor"
|
||||
strokeWidth="35.3"
|
||||
/>
|
||||
<path
|
||||
d="M201.3 35.5002L141 70.3002V0.700195L201.3 35.5002Z"
|
||||
fill="currentColor"
|
||||
stroke="currentColor"
|
||||
strokeWidth="0.4"
|
||||
/>
|
||||
<path
|
||||
d="M1 85.5C6.9 85.5 29.6 90.5 41.4 97.2C53.1 103.9 53.1 103.9 77.4 121.2C108.2 143 129.9 135.7 165.6 135.7"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M1 85.5C6.9 85.5 29.6 90.5 41.4 97.2C53.1 103.9 53.1 103.9 77.4 121.2C108.2 143 129.9 135.7 165.6 135.7"
|
||||
stroke="currentColor"
|
||||
strokeWidth="35.3"
|
||||
/>
|
||||
<path
|
||||
d="M200.1 135.4L139.8 100.7V170.2L200.1 135.4Z"
|
||||
fill="currentColor"
|
||||
stroke="currentColor"
|
||||
strokeWidth="0.4"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -29,6 +29,8 @@ import { Grape } from './Grape';
|
||||
import Idea from './Idea';
|
||||
import LinkedIn from './LinkedIn';
|
||||
import More from './More';
|
||||
import OpenRouter from './OpenRouter';
|
||||
import Ollama from './Ollama';
|
||||
import Refresh from './Refresh';
|
||||
import SensitiveHidden from './SensitiveHidden';
|
||||
import SensitiveVisible from './SensitiveVisible';
|
||||
@@ -80,6 +82,8 @@ export {
|
||||
LinkedIn,
|
||||
Microphone,
|
||||
More,
|
||||
OpenRouter,
|
||||
Ollama,
|
||||
Refresh,
|
||||
SensitiveHidden,
|
||||
SensitiveVisible,
|
||||
|
||||
@@ -14,7 +14,7 @@ const GridLayout = memo(function GridLayout({ children }: { children: React.Reac
|
||||
className="grid gap-4 [&_*]:z-20 p-1"
|
||||
style={{
|
||||
gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 200px))',
|
||||
justifyContent: 'start',
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -5,7 +5,6 @@ import ProviderGrid from './ProviderGrid';
|
||||
import { useConfig } from '../../ConfigContext';
|
||||
import { ProviderDetails } from '../../../api/types.gen';
|
||||
import { initializeSystem } from '../../../utils/providerUtils';
|
||||
import WelcomeGooseLogo from '../../WelcomeGooseLogo';
|
||||
import { toastService } from '../../../toasts';
|
||||
|
||||
interface ProviderSettingsProps {
|
||||
@@ -97,31 +96,25 @@ export default function ProviderSettings({ onClose, isOnboarding }: ProviderSett
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="h-screen w-full flex flex-col">
|
||||
<div className="h-screen w-full flex flex-col bg-background-default text-text-default">
|
||||
<ScrollArea className="flex-1 w-full">
|
||||
{isOnboarding && (
|
||||
<div className="w-full max-w-6xl mx-auto mt-16">
|
||||
<div className="group/logo flex justify-left px-4 sm:px-6 md:px-8">
|
||||
<WelcomeGooseLogo className="h-16 w-16 md:h-20 md:w-20 text-black dark:text-white" />
|
||||
<div className="w-full max-w-6xl mx-auto px-4 sm:px-6 md:px-8 pt-12 pb-4">
|
||||
{/* Consistent header pattern with back button */}
|
||||
<div className="flex flex-col pb-8 border-b border-border-default">
|
||||
<div className="flex items-center pt-2 mb-1 no-drag">
|
||||
<BackButton onClick={onClose} />
|
||||
</div>
|
||||
<h1 className="text-4xl font-light mb-4 pt-6" data-testid="provider-selection-heading">
|
||||
{isOnboarding ? 'Other providers' : 'Provider Configuration Settings'}
|
||||
</h1>
|
||||
{isOnboarding && (
|
||||
<p className="text-sm sm:text-base text-text-muted max-w-full sm:max-w-2xl">
|
||||
Select an AI model provider to get started with goose. You'll need to use API keys
|
||||
generated by each provider which will be encrypted and stored locally. You can
|
||||
change your provider at any time in settings.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className="w-full max-w-6xl mx-auto px-4 sm:px-6 md:px-8 pt-6 pb-4">
|
||||
{/* Only show back button if not in onboarding mode */}
|
||||
{!isOnboarding && <BackButton className={'mt-[42px]'} onClick={onClose} />}
|
||||
<h1
|
||||
className="text-2xl sm:text-3xl font-medium text-textStandard mt-4"
|
||||
data-testid="provider-selection-heading"
|
||||
>
|
||||
{isOnboarding ? 'Configure your providers' : 'Provider Configuration Settings'}
|
||||
</h1>
|
||||
{isOnboarding && (
|
||||
<p className="text-sm sm:text-base text-textSubtle max-w-full sm:max-w-2xl pt-2">
|
||||
Select an AI model provider to get started with goose. You'll need to use API keys
|
||||
generated by each provider which will be encrypted and stored locally. You can change
|
||||
your provider at any time in settings.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="py-8 pt-[20px]">
|
||||
|
||||
@@ -741,3 +741,32 @@ p > code.bg-inline-code {
|
||||
[data-state='collapsed'] [data-slot='sidebar-gap'] {
|
||||
will-change: width;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* Subtle back-and-forth shimmer animation for onboarding card */
|
||||
@keyframes shimmer {
|
||||
0% {
|
||||
transform: translateX(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
20% {
|
||||
opacity: 0.15;
|
||||
}
|
||||
50% {
|
||||
transform: translateX(100%);
|
||||
opacity: 0.05;
|
||||
}
|
||||
80% {
|
||||
opacity: 0.15;
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-100%);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.animate-shimmer {
|
||||
animation: shimmer 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user