Spaces:
Running
Running
| <html lang="en" data-theme="antigravity" class="dark"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Antigravity Console</title> | |
| <link rel="icon" type="image/svg+xml" href="favicon.svg"> | |
| <!-- Libraries --> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <!-- Alpine.js must be deferred so stores register their listeners first --> | |
| <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script> | |
| <!-- Compiled Tailwind CSS (includes DaisyUI) --> | |
| <link rel="stylesheet" href="css/style.css"> | |
| </head> | |
| <body | |
| class="bg-space-950 text-gray-300 font-sans antialiased min-h-screen overflow-hidden selection:bg-neon-purple selection:text-white" | |
| x-cloak x-data="app" x-init="if(window.UILogger) window.UILogger.debug('App initialized')"> | |
| <!-- Toast Notification --> | |
| <div class="fixed top-4 right-4 z-[100] flex flex-col gap-2 pointer-events-none"> | |
| <template x-if="$store.global.toast"> | |
| <div x-transition:enter="transition ease-out duration-300" | |
| x-transition:enter-start="opacity-0 translate-x-8 scale-95" | |
| x-transition:enter-end="opacity-100 translate-x-0 scale-100" | |
| x-transition:leave="transition ease-in duration-200" | |
| x-transition:leave-start="opacity-100 translate-x-0 scale-100" | |
| x-transition:leave-end="opacity-0 translate-x-4 scale-95" | |
| class="alert shadow-lg border backdrop-blur-md pointer-events-auto min-w-[300px]" :class="{ | |
| 'alert-info border-neon-cyan/20 bg-space-900/90 text-neon-cyan': $store.global.toast.type === 'info', | |
| 'alert-success border-neon-green/20 bg-space-900/90 text-neon-green': $store.global.toast.type === 'success', | |
| 'alert-error border-red-500/20 bg-space-900/90 text-red-400': $store.global.toast.type === 'error' | |
| }"> | |
| <div class="flex items-center gap-3"> | |
| <!-- Icons based on type --> | |
| <template x-if="$store.global.toast.type === 'info'"> | |
| <svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> | |
| </svg> | |
| </template> | |
| <template x-if="$store.global.toast.type === 'success'"> | |
| <svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" /> | |
| </svg> | |
| </template> | |
| <template x-if="$store.global.toast.type === 'error'"> | |
| <svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> | |
| </svg> | |
| </template> | |
| <span x-text="$store.global.toast.message" class="font-mono text-sm"></span> | |
| </div> | |
| </div> | |
| </template> | |
| </div> | |
| <!-- Navbar --> | |
| <div | |
| class="h-14 border-b border-space-border flex items-center px-4 lg:px-6 justify-between bg-space-900/50 backdrop-blur-md z-50 relative"> | |
| <div class="flex items-center gap-3"> | |
| <!-- Mobile Menu Button --> | |
| <button @click="toggleSidebar()" class="text-gray-400 hover:text-white focus:outline-none p-1 transition-colors"> | |
| <svg class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" /> | |
| </svg> | |
| </button> | |
| <div | |
| class="w-8 h-8 rounded bg-gradient-to-br from-neon-purple to-blue-600 flex items-center justify-center text-white font-bold shadow-[0_0_15px_rgba(168,85,247,0.4)]"> | |
| AG</div> | |
| <div class="flex flex-col"> | |
| <span class="text-sm font-bold tracking-wide text-white" | |
| x-text="$store.global.t('systemName')">ANTIGRAVITY</span> | |
| <div class="flex items-center gap-1.5"> | |
| <span class="text-[10px] text-gray-500 font-mono tracking-wider" | |
| x-text="$store.global.t('systemDesc')">CLAUDE PROXY SYSTEM</span> | |
| <span class="text-[10px] text-gray-500 font-mono tracking-wider" | |
| x-text="'v' + $store.global.version">v1.0.0</span> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex items-center gap-4"> | |
| <!-- Connection Pill --> | |
| <div class="flex items-center gap-2 px-3 py-1 rounded-full text-xs font-mono border transition-all duration-300" | |
| :class="connectionStatus === 'connected' | |
| ? 'bg-neon-green/10 border-neon-green/20 text-neon-green' | |
| : (connectionStatus === 'connecting' ? 'bg-yellow-500/10 border-yellow-500/20 text-yellow-500' : 'bg-red-500/10 border-red-500/20 text-red-500')"> | |
| <div class="w-1.5 h-1.5 rounded-full" | |
| :class="connectionStatus === 'connected' ? 'bg-neon-green shadow-[0_0_8px_rgba(34,197,94,0.6)]' : (connectionStatus === 'connecting' ? 'bg-yellow-500 animate-pulse' : 'bg-red-500')"> | |
| </div> | |
| <span | |
| x-text="connectionStatus === 'connected' ? $store.global.t('online') : (connectionStatus === 'disconnected' ? $store.global.t('offline') : $store.global.t('connecting'))"></span> | |
| </div> | |
| <div class="h-4 w-px bg-space-border"></div> | |
| <!-- Refresh Button --> | |
| <button type="button" class="btn btn-ghost btn-xs btn-square text-gray-400 hover:text-white hover:bg-white/5" | |
| @click="fetchData" :disabled="loading" :title="$store.global.t('refreshData')" aria-label="Refresh data"> | |
| <svg class="w-4 h-4" :class="{'animate-spin': loading}" fill="none" stroke="currentColor" | |
| viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Layout --> | |
| <div class="flex h-[calc(100vh-56px)] relative"> | |
| <!-- Mobile Sidebar Overlay --> | |
| <div x-show="sidebarOpen" | |
| x-transition:enter="transition-opacity ease-linear duration-300" | |
| x-transition:enter-start="opacity-0" | |
| x-transition:enter-end="opacity-100" | |
| x-transition:leave="transition-opacity ease-linear duration-300" | |
| x-transition:leave-start="opacity-100" | |
| x-transition:leave-end="opacity-0" | |
| @click="sidebarOpen = false" | |
| class="fixed inset-0 bg-black/50 z-40 lg:hidden" | |
| style="display: none;"></div> | |
| <!-- Sidebar --> | |
| <div class="fixed top-14 bottom-0 left-0 z-40 bg-space-900 border-r border-space-border transition-all duration-300 shadow-2xl overflow-hidden lg:static lg:h-auto lg:shadow-none lg:flex-shrink-0" | |
| :class="{ | |
| 'translate-x-0': sidebarOpen, | |
| '-translate-x-full': !sidebarOpen, | |
| 'w-64': sidebarOpen, | |
| 'lg:translate-x-0': sidebarOpen, | |
| 'lg:w-64': sidebarOpen, | |
| 'sidebar-collapsed': !sidebarOpen | |
| }"> | |
| <!-- Inner Sidebar Content (Fixed Width to prevent squashing) --> | |
| <div class="w-64 flex flex-col h-full pt-6 pb-4 flex-shrink-0"> | |
| <!-- Mobile Menu Header --> | |
| <div class="flex items-center justify-between px-4 mb-6 lg:hidden"> | |
| <span class="text-sm font-bold text-white" x-text="$store.global.t('menu')">Menu</span> | |
| <button @click="sidebarOpen = false" class="text-gray-400 hover:text-white"> | |
| <svg class="w-5 h-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> | |
| </svg> | |
| </button> | |
| </div> | |
| <!-- Desktop Header (Main) --> | |
| <div class="px-4 mb-2 text-xs font-bold text-gray-600 uppercase tracking-widest hidden lg:block" | |
| x-text="$store.global.t('main')">Main</div> | |
| <nav class="flex flex-col gap-1"> | |
| <button | |
| class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5" | |
| :class="{'active': $store.global.activeTab === 'dashboard'}" | |
| @click="$store.global.activeTab = 'dashboard'"> | |
| <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z" /> | |
| </svg> | |
| <span x-text="$store.global.t('dashboard')">Dashboard</span> | |
| </button> | |
| <button | |
| class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5" | |
| :class="{'active': $store.global.activeTab === 'models'}" | |
| @click="$store.global.activeTab = 'models'"> | |
| <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4" /> | |
| </svg> | |
| <span x-text="$store.global.t('models')">Models</span> | |
| </button> | |
| <button | |
| class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5" | |
| :class="{'active': $store.global.activeTab === 'accounts'}" | |
| @click="$store.global.activeTab = 'accounts'"> | |
| <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" /> | |
| </svg> | |
| <span x-text="$store.global.t('accounts')">Accounts</span> | |
| </button> | |
| </nav> | |
| <div class="px-4 mt-8 mb-2 text-xs font-bold text-gray-600 uppercase tracking-widest" | |
| x-text="$store.global.t('system')">System</div> | |
| <nav class="flex flex-col gap-1"> | |
| <button | |
| class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5" | |
| :class="{'active': $store.global.activeTab === 'logs'}" @click="$store.global.activeTab = 'logs'"> | |
| <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" /> | |
| </svg> | |
| <span x-text="$store.global.t('logs')">Logs</span> | |
| </button> | |
| <button | |
| class="nav-item flex items-center gap-3 px-6 py-3 text-sm font-medium text-gray-400 hover:text-white hover:bg-white/5" | |
| :class="{'active': $store.global.activeTab === 'settings'}" | |
| @click="$store.global.activeTab = 'settings'"> | |
| <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" /> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" | |
| d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> | |
| </svg> | |
| <span x-text="$store.global.t('settings')">Settings</span> | |
| </button> | |
| </nav> | |
| <!-- Footer Info --> | |
| <div class="mt-auto px-6 text-xs text-gray-400 font-mono"> | |
| <div class="flex justify-center"> | |
| <a href="https://github.com/badri-s2001/antigravity-claude-proxy" target="_blank" rel="noopener noreferrer" | |
| class="flex items-center gap-1.5 hover:text-neon-purple transition-colors"> | |
| <svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"> | |
| <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"/> | |
| </svg> | |
| <span x-text="$store.global.t('github')">GitHub</span> | |
| </a> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Main Content --> | |
| <div class="flex-1 overflow-auto bg-space-950 relative custom-scrollbar"> | |
| <!-- Views Container --> | |
| <!-- Dashboard --> | |
| <div x-show="$store.global.activeTab === 'dashboard'" x-load-view="'dashboard'" | |
| x-transition:enter="fade-enter-active" x-transition:enter-start="fade-enter-from" | |
| class="w-full"></div> | |
| <!-- Models --> | |
| <div x-show="$store.global.activeTab === 'models'" x-load-view="'models'" | |
| x-transition:enter="fade-enter-active" x-transition:enter-start="fade-enter-from" | |
| class="w-full"></div> | |
| <!-- Logs --> | |
| <div x-show="$store.global.activeTab === 'logs'" x-load-view="'logs'" x-transition:enter="fade-enter-active" | |
| x-transition:enter-start="fade-enter-from" class="w-full h-full"></div> | |
| <!-- Accounts --> | |
| <div x-show="$store.global.activeTab === 'accounts'" x-load-view="'accounts'" | |
| x-transition:enter="fade-enter-active" x-transition:enter-start="fade-enter-from" | |
| class="w-full"></div> | |
| <!-- Settings --> | |
| <div x-show="$store.global.activeTab === 'settings'" x-load-view="'settings'" | |
| x-transition:enter="fade-enter-active" x-transition:enter-start="fade-enter-from" | |
| class="w-full"></div> | |
| </div> | |
| </div> | |
| <!-- Add Account Modal --> | |
| <dialog id="add_account_modal" class="modal backdrop-blur-sm" x-data="addAccountModal"> | |
| <div class="modal-box max-w-lg w-full bg-space-900 border border-space-border text-gray-300 shadow-[0_0_50px_rgba(0,0,0,0.5)] p-6"> | |
| <h3 class="font-bold text-lg text-white mb-4" x-text="$store.global.t('addAccount')">Add New Account</h3> | |
| <div class="flex flex-col gap-4"> | |
| <p class="text-sm text-gray-400 leading-relaxed" x-text="$store.global.t('connectGoogleDesc')">Connect a Google | |
| Workspace account to increase your API quota limit. | |
| The account will be used to proxy Claude requests via Antigravity.</p> | |
| <button class="btn btn-primary flex items-center justify-center gap-3 h-11" @click="addAccountWeb"> | |
| <svg class="w-5 h-5" viewBox="0 0 24 24" fill="currentColor"> | |
| <path | |
| d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"> | |
| </path> | |
| <path | |
| d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"> | |
| </path> | |
| <path | |
| d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"> | |
| </path> | |
| <path | |
| d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"> | |
| </path> | |
| </svg> | |
| <span x-text="$store.global.t('connectGoogle')">Connect Google Account</span> | |
| </button> | |
| <div class="text-center mt-2"> | |
| <p class="text-xs text-gray-500 mb-2" x-text="$store.global.t('or')">OR</p> | |
| <!-- Manual Mode (collapsible) --> | |
| <details class="group mb-2" @toggle="initManualAuth($event)"> | |
| <summary class="text-xs text-gray-400 hover:text-neon-cyan cursor-pointer transition-colors inline-flex items-center gap-1"> | |
| <svg class="w-3 h-3 transition-transform group-open:rotate-90" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/> | |
| </svg> | |
| <span x-text="$store.global.t('manualMode')">Manual Mode</span> | |
| </summary> | |
| <div class="mt-3 p-3 bg-black/50 rounded border border-space-border/30 text-xs"> | |
| <template x-if="authUrl"> | |
| <div> | |
| <div class="flex items-center gap-2 mb-2"> | |
| <input type="text" readonly :value="authUrl" | |
| class="input input-xs input-bordered flex-1 bg-space-800 text-gray-300 font-mono"> | |
| <button class="btn btn-xs btn-ghost text-neon-cyan" @click="copyLink" :title="$store.global.t('linkCopied')"> | |
| <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/> | |
| </svg> | |
| </button> | |
| </div> | |
| <div class="flex items-center gap-2"> | |
| <input type="text" x-model="callbackInput" | |
| :placeholder="$store.global.t('pasteCallbackPlaceholder')" | |
| class="input input-xs input-bordered flex-1 bg-space-800 text-gray-300 font-mono"> | |
| <button class="btn btn-xs btn-success" | |
| @click="completeManualAuth" | |
| :disabled="!callbackInput || submitting" | |
| :class="{ 'loading': submitting }"> | |
| <span x-text="$store.global.t('completeAuth')">OK</span> | |
| </button> | |
| </div> | |
| </div> | |
| </template> | |
| <template x-if="!authUrl"> | |
| <div class="text-gray-500">Loading...</div> | |
| </template> | |
| </div> | |
| </details> | |
| <!-- CLI Command --> | |
| <details class="group"> | |
| <summary class="text-xs text-gray-400 hover:text-neon-cyan cursor-pointer transition-colors inline-flex items-center gap-1"> | |
| <svg class="w-3 h-3 transition-transform group-open:rotate-90" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/> | |
| </svg> | |
| <span x-text="$store.global.t('useCliCommand')">Use CLI Command</span> | |
| </summary> | |
| <div class="mt-3 p-3 bg-black/50 rounded border border-space-border/30 font-mono text-xs text-gray-300"> | |
| <div class="flex items-center gap-2"> | |
| <span class="text-gray-600">$</span> | |
| <code>npm run accounts:add</code> | |
| </div> | |
| </div> | |
| </details> | |
| </div> | |
| </div> | |
| <div class="modal-action mt-6"> | |
| <form method="dialog"> | |
| <button type="submit" class="btn btn-ghost hover:bg-white/10" @click="resetState()" x-text="$store.global.t('close')">Close</button> | |
| </form> | |
| </div> | |
| </div> | |
| <form method="dialog" class="modal-backdrop"> | |
| <button type="button" @click="resetState()" x-text="$store.global.t('close')">close</button> | |
| </form> | |
| </dialog> | |
| <!-- OAuth Progress Modal --> | |
| <dialog id="oauth_progress_modal" class="modal" :class="{ 'modal-open': $store.global.oauthProgress.active }"> | |
| <div class="modal-box bg-space-900 border border-neon-purple/50"> | |
| <h3 class="font-bold text-lg text-white flex items-center gap-2"> | |
| <svg class="w-6 h-6 text-neon-purple animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" /> | |
| </svg> | |
| <span x-text="$store.global.t('oauthWaiting')">Waiting for OAuth...</span> | |
| </h3> | |
| <p class="py-4 text-gray-400 text-sm" x-text="$store.global.t('oauthWaitingDesc')"> | |
| Please complete authentication in the popup window. | |
| </p> | |
| <!-- Progress Bar --> | |
| <div class="w-full bg-space-800 rounded-full h-2 mb-4 overflow-hidden"> | |
| <div class="bg-neon-purple h-2 rounded-full transition-all duration-500" | |
| :style="`width: ${($store.global.oauthProgress.current / $store.global.oauthProgress.max) * 100}%`"> | |
| </div> | |
| </div> | |
| <!-- Progress Text --> | |
| <div class="flex justify-between text-xs text-gray-600 mb-4"> | |
| <span x-text="`${$store.global.oauthProgress.current} / ${$store.global.oauthProgress.max}s`"></span> | |
| <span x-text="`${Math.round(($store.global.oauthProgress.current / $store.global.oauthProgress.max) * 100)}%`"></span> | |
| </div> | |
| <div class="modal-action"> | |
| <button class="btn btn-sm btn-ghost text-gray-400" | |
| @click="$store.global.oauthProgress.cancel && $store.global.oauthProgress.cancel()" | |
| x-text="$store.global.t('cancelOAuth')"> | |
| Cancel | |
| </button> | |
| </div> | |
| </div> | |
| </dialog> | |
| <!-- Scripts - Loading Order Matters! --> | |
| <!-- 1. Config & Utils (global helpers) --> | |
| <script src="js/config/constants.js"></script> | |
| <script src="js/utils/ui-logger.js"></script><!-- Issue #183: Conditional logging utility --> | |
| <script src="js/utils.js"></script> | |
| <script src="js/utils/error-handler.js"></script> | |
| <script src="js/utils/account-actions.js"></script> | |
| <script src="js/utils/validators.js"></script> | |
| <script src="js/utils/model-config.js"></script> | |
| <script src="js/utils/redact.js"></script> | |
| <!-- Translation files (must load before store.js) --> | |
| <script src="js/translations/en.js"></script> | |
| <script src="js/translations/zh.js"></script> | |
| <script src="js/translations/tr.js"></script> | |
| <script src="js/translations/id.js"></script> | |
| <script src="js/translations/pt.js"></script> | |
| <!-- 2. Alpine Stores (register alpine:init listeners) --> | |
| <script src="js/store.js"></script> | |
| <script src="js/data-store.js"></script> | |
| <script src="js/settings-store.js"></script> | |
| <!-- 3. Components (register to window.Components) --> | |
| <!-- Dashboard modules (load before main dashboard) --> | |
| <script src="js/components/dashboard/stats.js"></script> | |
| <script src="js/components/dashboard/charts.js"></script> | |
| <script src="js/components/dashboard/filters.js"></script> | |
| <script src="js/components/dashboard.js"></script> | |
| <script src="js/components/models.js"></script> | |
| <script src="js/components/account-manager.js"></script> | |
| <script src="js/components/claude-config.js"></script> | |
| <script src="js/components/model-dropdown.js"></script> | |
| <script src="js/components/logs-viewer.js"></script> | |
| <script src="js/components/server-config.js"></script> | |
| <script src="js/components/add-account-modal.js"></script> | |
| <!-- 4. App (registers Alpine components from window.Components) --> | |
| <script src="app.js"></script> | |
| </body> | |
| </html> |