Add provider setup wizard on first launch
Added first-run provider selection wizard with: - Google OAuth option (sign in with Google account) - Custom provider option (choose from 17+ presets) - 3-step setup wizard UI - Automatic provider configuration - Pre-configured Google Antigravity OAuth Files changed: - main.js: Added AI provider check and wizard display logic - preload.js: Added app:launch-main IPC handler - provider-setup-wizard.html: Complete wizard UI (new) - app.asar: Repacked with all changes Features: - Shows wizard if no AI provider configured - Google OAuth for easy setup - 17+ provider presets for custom setup - Automatic settings configuration - Professional UI with step indicators
This commit is contained in:
90
src/app-extracted/dist/main.js
vendored
90
src/app-extracted/dist/main.js
vendored
@@ -83,6 +83,77 @@ if (HEADLESS) {
|
||||
if (!electron_1.app.commandLine.hasSwitch('remote-debugging-port')) {
|
||||
electron_1.app.commandLine.appendSwitch('remote-debugging-port', '0');
|
||||
}
|
||||
// ---------------------------------------------------------------------------
|
||||
// AI Provider Setup Functions
|
||||
// ---------------------------------------------------------------------------
|
||||
let providerSetupWindow = null;
|
||||
|
||||
async function checkAIProviderSetup() {
|
||||
try {
|
||||
// Check if any provider is configured
|
||||
const items = await storageManager.getItems();
|
||||
const hasProvider = items['aiProvider'] && items['aiModel'];
|
||||
const hasProvidersConfig = items['aiProviders'];
|
||||
|
||||
// Return true if setup is complete
|
||||
return hasProvider || hasProvidersConfig;
|
||||
} catch (error) {
|
||||
console.error('Error checking AI provider setup:', error);
|
||||
return true; // Skip wizard if error
|
||||
}
|
||||
}
|
||||
|
||||
async function showProviderSetupWizard() {
|
||||
return new Promise((resolve) => {
|
||||
const wizardUrl = `file://${__dirname}/provider-setup-wizard.html`;
|
||||
|
||||
providerSetupWindow = new electron_1.BrowserWindow({
|
||||
width: 1000,
|
||||
height: 800,
|
||||
title: 'Antigravity - AI Provider Setup',
|
||||
icon: `${__dirname}/icon.png`,
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: false,
|
||||
contextIsolation: true,
|
||||
preload: `${__dirname}/preload.js`,
|
||||
},
|
||||
});
|
||||
|
||||
providerSetupWindow.loadFile(`${__dirname}/provider-setup-wizard.html`);
|
||||
|
||||
// Handle setup completion
|
||||
providerSetupWindow.webContents.on('did-finish-load', () => {
|
||||
console.log('Provider setup wizard loaded');
|
||||
});
|
||||
|
||||
// Listen for setup completion message
|
||||
electron_1.ipcMain.once('provider-setup:complete', (event, data) => {
|
||||
console.log('Provider setup completed:', data);
|
||||
if (providerSetupWindow) {
|
||||
providerSetupWindow.close();
|
||||
providerSetupWindow = null;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
|
||||
// Listen for setup cancelled
|
||||
electron_1.ipcMain.once('provider-setup:cancelled', () => {
|
||||
console.log('Provider setup cancelled');
|
||||
if (providerSetupWindow) {
|
||||
providerSetupWindow.close();
|
||||
providerSetupWindow = null;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
|
||||
providerSetupWindow.on('closed', () => {
|
||||
providerSetupWindow = null;
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Application Lifecycle
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -155,8 +226,27 @@ electron_1.app
|
||||
pendingDeepLink = null; // Clear after read
|
||||
return link;
|
||||
});
|
||||
|
||||
// Check if AI providers are configured - show wizard if not
|
||||
const aiProviderSetupComplete = await checkAIProviderSetup();
|
||||
if (!aiProviderSetupComplete && !HEADLESS) {
|
||||
await showProviderSetupWizard();
|
||||
return; // Wait for wizard to complete
|
||||
}
|
||||
// Handle requests coming from custom schemes
|
||||
(0, customScheme_1.registerCustomSchemeHandlers)();
|
||||
|
||||
// Handler for launching main app after provider setup
|
||||
electron_1.ipcMain.handle('app:launch-main', async () => {
|
||||
// Resume the app startup
|
||||
hasStartedMainApplication = false;
|
||||
// Restart the initialization process
|
||||
await startMainApplication();
|
||||
});
|
||||
/**
|
||||
* Main application startup function
|
||||
*/
|
||||
async function startMainApplication() {
|
||||
// Set About panel options with LS CL
|
||||
const cl = await (0, languageServer_1.getLsCL)();
|
||||
electron_1.app.setAboutPanelOptions({
|
||||
|
||||
5
src/app-extracted/dist/preload.js
vendored
5
src/app-extracted/dist/preload.js
vendored
@@ -93,6 +93,10 @@ const electronNativeAPI = {
|
||||
},
|
||||
openExternal: (url) => electron_1.ipcRenderer.invoke('shell:open-external', url),
|
||||
};
|
||||
|
||||
const appAPI = {
|
||||
launchMain: () => electron_1.ipcRenderer.invoke('app:launch-main'),
|
||||
};
|
||||
electron_1.contextBridge.exposeInMainWorld('electronUpdater', updaterAPI);
|
||||
electron_1.contextBridge.exposeInMainWorld('dialog', dialogAPI);
|
||||
electron_1.contextBridge.exposeInMainWorld('nativeNotifications', notificationAPI);
|
||||
@@ -102,3 +106,4 @@ electron_1.contextBridge.exposeInMainWorld('extensions', extensionsAPI);
|
||||
electron_1.contextBridge.exposeInMainWorld('deepLink', deepLinkAPI);
|
||||
electron_1.contextBridge.exposeInMainWorld('agent', agentAPI);
|
||||
electron_1.contextBridge.exposeInMainWorld('electronNative', electronNativeAPI);
|
||||
electron_1.contextBridge.exposeInMainWorld('app', appAPI);
|
||||
|
||||
812
src/app-extracted/dist/provider-setup-wizard.html
vendored
Normal file
812
src/app-extracted/dist/provider-setup-wizard.html
vendored
Normal file
@@ -0,0 +1,812 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Antigravity - AI Provider Setup</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
width: 100%;
|
||||
background: white;
|
||||
border-radius: 20px;
|
||||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 32px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.header p {
|
||||
opacity: 0.9;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.step-indicator {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.step {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.step.active {
|
||||
color: #667eea;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #e0e0e0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.step.active .step-number {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.step.completed .step-number {
|
||||
background: #4caf50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.options-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
gap: 24px;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.option-card {
|
||||
border: 3px solid #e0e0e0;
|
||||
border-radius: 16px;
|
||||
padding: 28px;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.option-card:hover {
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 8px 24px rgba(102, 126, 234, 0.2);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
.option-card.selected {
|
||||
border-color: #667eea;
|
||||
background: linear-gradient(135deg, rgba(102, 126, 234, 0.05) 0%, rgba(118, 75, 162, 0.05) 100%);
|
||||
}
|
||||
|
||||
.option-icon {
|
||||
font-size: 48px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.option-title {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.option-description {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.option-features {
|
||||
margin-top: 16px;
|
||||
padding-top: 16px;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.feature-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 13px;
|
||||
color: #666;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.feature-item::before {
|
||||
content: "✓";
|
||||
color: #4caf50;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.btn {
|
||||
padding: 14px 32px;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
|
||||
}
|
||||
|
||||
.btn-primary:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: #f0f0f0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.btn-secondary:hover {
|
||||
background: #e0e0e0;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
.action-buttons .btn-group {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.provider-form {
|
||||
background: #f8f9fa;
|
||||
border-radius: 12px;
|
||||
padding: 28px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.form-input:focus {
|
||||
outline: none;
|
||||
border-color: #667eea;
|
||||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||||
}
|
||||
|
||||
.form-select {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.preset-section {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.preset-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 12px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.preset-card {
|
||||
border: 2px solid #e0e0e0;
|
||||
border-radius: 8px;
|
||||
padding: 16px;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.preset-card:hover {
|
||||
border-color: #667eea;
|
||||
background: rgba(102, 126, 234, 0.05);
|
||||
}
|
||||
|
||||
.preset-card.selected {
|
||||
border-color: #667eea;
|
||||
background: linear-gradient(135deg, rgba(102, 126, 234, 0.1) 0%, rgba(118, 75, 162, 0.1) 100%);
|
||||
}
|
||||
|
||||
.preset-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.preset-models {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.success-screen {
|
||||
text-align: center;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.success-icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.success-title {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.success-description {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 3px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
border-top-color: white;
|
||||
animation: spin 1s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.loading-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.loading-content {
|
||||
background: white;
|
||||
padding: 40px;
|
||||
border-radius: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loading-content p {
|
||||
margin-top: 16px;
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="loading-overlay" class="loading-overlay hidden">
|
||||
<div class="loading-content">
|
||||
<div class="spinner"></div>
|
||||
<p id="loading-message">Setting up...</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>🤖 Welcome to Antigravity</h1>
|
||||
<p>Choose how you want to connect to AI providers</p>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
<!-- Step 1: Choose Setup Method -->
|
||||
<div id="step1" class="setup-step">
|
||||
<div class="step-indicator">
|
||||
<div class="step active">
|
||||
<div class="step-number">1</div>
|
||||
<span>Choose Setup</span>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">2</div>
|
||||
<span>Configure</span>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">3</div>
|
||||
<span>Complete</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="options-grid">
|
||||
<!-- Google OAuth -->
|
||||
<div class="option-card" onclick="selectOption('google-oauth')" data-option="google-oauth">
|
||||
<div class="option-icon">🔐</div>
|
||||
<div class="option-title">Google OAuth</div>
|
||||
<div class="option-description">
|
||||
Sign in with your Google account to access Google AI services including Gemini and Antigravity models.
|
||||
</div>
|
||||
<div class="option-features">
|
||||
<div class="feature-item">Easy one-click setup</div>
|
||||
<div class="feature-item">Access to Gemini models</div>
|
||||
<div class="feature-item">Antigravity-specific models</div>
|
||||
<div class="feature-item">No API key needed</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom Provider -->
|
||||
<div class="option-card" onclick="selectOption('custom-provider')" data-option="custom-provider">
|
||||
<div class="option-icon">⚙️</div>
|
||||
<div class="option-title">Custom Provider</div>
|
||||
<div class="option-description">
|
||||
Use your own API key to connect to any AI provider. Choose from 17+ pre-configured providers.
|
||||
</div>
|
||||
<div class="option-features">
|
||||
<div class="feature-item">17+ provider presets</div>
|
||||
<div class="feature-item">Bring your own API key</div>
|
||||
<div class="feature-item">Full control over settings</div>
|
||||
<div class="feature-item">OpenAI, Anthropic, and more</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<div></div>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-primary" id="next-btn" disabled onclick="goToStep2()">
|
||||
Next →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2: Configure Provider -->
|
||||
<div id="step2" class="setup-step hidden">
|
||||
<div class="step-indicator">
|
||||
<div class="step completed">
|
||||
<div class="step-number">✓</div>
|
||||
<span>Choose Setup</span>
|
||||
</div>
|
||||
<div class="step active">
|
||||
<div class="step-number">2</div>
|
||||
<span>Configure</span>
|
||||
</div>
|
||||
<div class="step">
|
||||
<div class="step-number">3</div>
|
||||
<span>Complete</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Google OAuth Configuration -->
|
||||
<div id="google-config" class="hidden">
|
||||
<h2 style="font-size: 24px; color: #333; margin-bottom: 20px;">🔐 Google Account Setup</h2>
|
||||
<p style="font-size: 16px; color: #666; margin-bottom: 30px;">
|
||||
Click the button below to sign in with your Google account. You'll be redirected to Google's OAuth page.
|
||||
</p>
|
||||
|
||||
<div style="text-align: center; margin: 40px 0;">
|
||||
<button class="btn btn-primary" style="font-size: 18px; padding: 16px 40px;" onclick="startGoogleOAuth()">
|
||||
🔐 Sign in with Google
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style="background: #f0f7ff; border-left: 4px solid #667eea; padding: 16px; border-radius: 4px; margin: 20px 0;">
|
||||
<strong>What you'll get:</strong>
|
||||
<ul style="margin: 12px 0 0 20px; line-height: 1.8;">
|
||||
<li>Access to Google Gemini models</li>
|
||||
<li>Antigravity-specific models (antigravity-gemini-3-flash, etc.)</li>
|
||||
<li>Claude Sonnet via Antigravity</li>
|
||||
<li>And more...</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Custom Provider Configuration -->
|
||||
<div id="custom-config" class="hidden">
|
||||
<h2 style="font-size: 24px; color: #333; margin-bottom: 20px;">⚙️ Custom Provider Setup</h2>
|
||||
|
||||
<div class="preset-section">
|
||||
<label class="form-label">Choose a Provider Preset</label>
|
||||
<div class="preset-grid" id="preset-grid">
|
||||
<!-- Presets will be loaded here -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="provider-form" id="provider-form" style="margin-top: 24px;">
|
||||
<div class="form-title" id="form-title">Configure Provider</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Provider Name</label>
|
||||
<input type="text" id="provider-name" class="form-input" placeholder="My Custom Provider">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">API Type</label>
|
||||
<select id="provider-type" class="form-select">
|
||||
<option value="openai">OpenAI (GPT-4, GPT-3.5)</option>
|
||||
<option value="anthropic">Anthropic (Claude)</option>
|
||||
<option value="google_gemini">Google Gemini</option>
|
||||
<option value="google_antigravity">Google Antigravity</option>
|
||||
<option value="ollama">Ollama (Local)</option>
|
||||
<option value="groq">Groq</option>
|
||||
<option value="openrouter">OpenRouter</option>
|
||||
<option value="custom">Custom</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">API Endpoint</label>
|
||||
<input type="text" id="provider-endpoint" class="form-input" placeholder="https://api.openai.com/v1">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">API Key</label>
|
||||
<input type="password" id="provider-apikey" class="form-input" placeholder="sk-...">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="form-label">Default Model</label>
|
||||
<input type="text" id="provider-model" class="form-input" placeholder="gpt-4o">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<button class="btn btn-secondary" onclick="goToStep1()">
|
||||
← Back
|
||||
</button>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-primary" id="setup-btn" onclick="completeSetup()">
|
||||
Complete Setup
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 3: Success -->
|
||||
<div id="step3" class="setup-step hidden">
|
||||
<div class="success-screen">
|
||||
<div class="success-icon">🎉</div>
|
||||
<div class="success-title">Setup Complete!</div>
|
||||
<div class="success-description">
|
||||
Antigravity is ready to use with your AI provider.
|
||||
</div>
|
||||
<button class="btn btn-primary" style="font-size: 18px; padding: 16px 40px;" onclick="launchAntigravity()">
|
||||
🚀 Launch Antigravity
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
let selectedOption = null;
|
||||
let selectedPreset = null;
|
||||
let providers = [];
|
||||
|
||||
// Initialize
|
||||
async function init() {
|
||||
try {
|
||||
// Load available presets
|
||||
const presets = await window.electron.invoke('ai:get-available-presets');
|
||||
renderPresets(presets);
|
||||
|
||||
// Load existing providers
|
||||
providers = await window.electron.invoke('ai:get-providers');
|
||||
} catch (error) {
|
||||
console.error('Error initializing:', error);
|
||||
showError('Failed to initialize. Please restart the application.');
|
||||
}
|
||||
}
|
||||
|
||||
function selectOption(option) {
|
||||
selectedOption = option;
|
||||
|
||||
// Update UI
|
||||
document.querySelectorAll('.option-card').forEach(card => {
|
||||
card.classList.remove('selected');
|
||||
});
|
||||
document.querySelector(`[data-option="${option}"]`).classList.add('selected');
|
||||
|
||||
// Enable next button
|
||||
document.getElementById('next-btn').disabled = false;
|
||||
}
|
||||
|
||||
function goToStep1() {
|
||||
document.querySelectorAll('.setup-step').forEach(step => step.classList.add('hidden'));
|
||||
document.getElementById('step1').classList.remove('hidden');
|
||||
updateStepIndicators(1);
|
||||
}
|
||||
|
||||
function goToStep2() {
|
||||
if (!selectedOption) return;
|
||||
|
||||
document.querySelectorAll('.setup-step').forEach(step => step.classList.add('hidden'));
|
||||
document.getElementById('step2').classList.remove('hidden');
|
||||
updateStepIndicators(2);
|
||||
|
||||
// Show appropriate config
|
||||
document.getElementById('google-config').classList.toggle('hidden', selectedOption !== 'google-oauth');
|
||||
document.getElementById('custom-config').classList.toggle('hidden', selectedOption !== 'custom-provider');
|
||||
}
|
||||
|
||||
function goToStep3() {
|
||||
document.querySelectorAll('.setup-step').forEach(step => step.classList.add('hidden'));
|
||||
document.getElementById('step3').classList.remove('hidden');
|
||||
updateStepIndicators(3);
|
||||
}
|
||||
|
||||
function updateStepIndicators(currentStep) {
|
||||
document.querySelectorAll('.step').forEach((step, index) => {
|
||||
step.classList.remove('active', 'completed');
|
||||
if (index + 1 < currentStep) {
|
||||
step.classList.add('completed');
|
||||
step.querySelector('.step-number').textContent = '✓';
|
||||
} else if (index + 1 === currentStep) {
|
||||
step.classList.add('active');
|
||||
step.querySelector('.step-number').textContent = index + 1;
|
||||
} else {
|
||||
step.querySelector('.step-number').textContent = index + 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderPresets(presets) {
|
||||
const grid = document.getElementById('preset-grid');
|
||||
|
||||
// Group presets by category
|
||||
const categories = {
|
||||
'Google': presets.filter(p => p.toLowerCase().includes('google') || p.toLowerCase().includes('gemini') || p.toLowerCase().includes('antigravity')),
|
||||
'OpenAI': presets.filter(p => p.toLowerCase().includes('openai')),
|
||||
'Anthropic': presets.filter(p => p.toLowerCase().includes('anthropic')),
|
||||
'OpenRouter': presets.filter(p => p.toLowerCase().includes('router')),
|
||||
'Other': presets.filter(p =>
|
||||
!p.toLowerCase().includes('google') &&
|
||||
!p.toLowerCase().includes('gemini') &&
|
||||
!p.toLowerCase().includes('antigravity') &&
|
||||
!p.toLowerCase().includes('openai') &&
|
||||
!p.toLowerCase().includes('anthropic') &&
|
||||
!p.toLowerCase().includes('router')
|
||||
)
|
||||
};
|
||||
|
||||
grid.innerHTML = Object.entries(categories)
|
||||
.filter(([name, items]) => items.length > 0)
|
||||
.map(([category, items]) => `
|
||||
${items.map(preset => `
|
||||
<div class="preset-card" onclick="selectPreset('${preset}')" data-preset="${preset}">
|
||||
<div class="preset-name">${preset}</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function selectPreset(presetName) {
|
||||
selectedPreset = presetName;
|
||||
|
||||
// Update UI
|
||||
document.querySelectorAll('.preset-card').forEach(card => {
|
||||
card.classList.remove('selected');
|
||||
});
|
||||
document.querySelector(`[data-preset="${presetName}"]`).classList.add('selected');
|
||||
|
||||
// Get preset details
|
||||
window.electron.invoke('ai:get-preset', presetName).then(preset => {
|
||||
if (preset) {
|
||||
document.getElementById('form-title').textContent = `Configure: ${presetName}`;
|
||||
document.getElementById('provider-name').value = presetName;
|
||||
document.getElementById('provider-type').value = preset.type || 'custom';
|
||||
document.getElementById('provider-endpoint').value = preset.endpoint || '';
|
||||
|
||||
if (preset.models && preset.models.length > 0) {
|
||||
document.getElementById('provider-model').value = preset.models[0];
|
||||
}
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error('Error loading preset:', err);
|
||||
});
|
||||
}
|
||||
|
||||
function startGoogleOAuth() {
|
||||
showLoading('Starting Google OAuth flow...');
|
||||
|
||||
// Add Google Antigravity OAuth provider
|
||||
window.electron.invoke('ai:add-provider', {
|
||||
name: 'Google Antigravity (OAuth)',
|
||||
type: 'google_antigravity',
|
||||
endpoint: 'https://daily-cloudcode-pa.sandbox.googleapis.com',
|
||||
apiKey: 'oauth',
|
||||
models: [
|
||||
'antigravity-gemini-3-flash',
|
||||
'antigravity-gemini-3-pro',
|
||||
'antigravity-gemini-3.1-pro',
|
||||
'antigravity-claude-sonnet-4-6',
|
||||
'antigravity-claude-opus-4-6-thinking',
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro'
|
||||
],
|
||||
defaultModel: 'antigravity-gemini-3-flash',
|
||||
capabilities: ['chat', 'vision', 'tool_use', 'streaming']
|
||||
}).then(() => {
|
||||
hideLoading();
|
||||
showSuccess('Google Antigravity OAuth configured successfully!');
|
||||
setTimeout(() => goToStep3(), 1500);
|
||||
}).catch(err => {
|
||||
hideLoading();
|
||||
showError('Failed to configure Google OAuth: ' + err.message);
|
||||
});
|
||||
}
|
||||
|
||||
async function completeSetup() {
|
||||
if (selectedOption === 'google-oauth') {
|
||||
startGoogleOAuth();
|
||||
return;
|
||||
}
|
||||
|
||||
// Custom provider setup
|
||||
const name = document.getElementById('provider-name').value.trim();
|
||||
const endpoint = document.getElementById('provider-endpoint').value.trim();
|
||||
const apiKey = document.getElementById('provider-apikey').value.trim();
|
||||
const model = document.getElementById('provider-model').value.trim();
|
||||
const type = document.getElementById('provider-type').value;
|
||||
|
||||
if (!name || !endpoint) {
|
||||
showError('Please fill in all required fields.');
|
||||
return;
|
||||
}
|
||||
|
||||
showLoading('Setting up your provider...');
|
||||
|
||||
try {
|
||||
await window.electron.invoke('ai:add-provider', {
|
||||
name: name,
|
||||
type: type,
|
||||
endpoint: endpoint,
|
||||
apiKey: apiKey,
|
||||
models: model ? [model] : [],
|
||||
defaultModel: model,
|
||||
capabilities: ['chat', 'streaming']
|
||||
});
|
||||
|
||||
// Set as default provider
|
||||
const providers = await window.electron.invoke('ai:get-providers');
|
||||
const newProvider = providers[providers.length - 1];
|
||||
|
||||
if (newProvider) {
|
||||
await window.electron.invoke('ai:set-default-provider', newProvider.id);
|
||||
|
||||
// Update settings
|
||||
await window.electron.invoke('storage:update-items', {
|
||||
'aiProvider': newProvider.id,
|
||||
'aiModel': model || 'gpt-4o',
|
||||
'aiTemperature': '0.7',
|
||||
'aiMaxTokens': '4096',
|
||||
'aiStreaming': 'true'
|
||||
});
|
||||
}
|
||||
|
||||
hideLoading();
|
||||
showSuccess('Provider configured successfully!');
|
||||
setTimeout(() => goToStep3(), 1500);
|
||||
} catch (error) {
|
||||
hideLoading();
|
||||
showError('Failed to setup provider: ' + error.message);
|
||||
}
|
||||
}
|
||||
|
||||
function launchAntigravity() {
|
||||
showLoading('Launching Antigravity...');
|
||||
|
||||
// Close this window and launch main app
|
||||
window.electron.invoke('app:launch-main').then(() => {
|
||||
window.close();
|
||||
}).catch(err => {
|
||||
hideLoading();
|
||||
showError('Failed to launch Antigravity: ' + err.message);
|
||||
});
|
||||
}
|
||||
|
||||
function showLoading(message) {
|
||||
document.getElementById('loading-message').textContent = message;
|
||||
document.getElementById('loading-overlay').classList.remove('hidden');
|
||||
}
|
||||
|
||||
function hideLoading() {
|
||||
document.getElementById('loading-overlay').classList.add('hidden');
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
alert('Error: ' + message);
|
||||
}
|
||||
|
||||
function showSuccess(message) {
|
||||
alert('Success: ' + message);
|
||||
}
|
||||
|
||||
// Initialize on load
|
||||
init();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
BIN
src/app.asar
BIN
src/app.asar
Binary file not shown.
Reference in New Issue
Block a user