Spaces:
Running
Running
| <script lang="ts"> | |
| import ApiKeyInput from '$lib/components/ApiKeyInput.svelte'; | |
| import StoryEngine from '$lib/components/StoryEngine.svelte'; | |
| import { apiKey, isGenerating, generationError, resetStory } from '$lib/stores/story'; | |
| let showEngine = false; | |
| let errorMessage = ''; | |
| function handleApiKeySet() { | |
| showEngine = true; | |
| errorMessage = ''; | |
| } | |
| function handleEngineError(error: string) { | |
| errorMessage = error; | |
| } | |
| function handleReset() { | |
| resetStory(); | |
| showEngine = false; | |
| errorMessage = ''; | |
| } | |
| </script> | |
| <div class="app"> | |
| <header class="app-header"> | |
| <h1>🎬 Odyssey</h1> | |
| <p class="subtitle">An interactive video-based choose-your-own-adventure</p> | |
| </header> | |
| <main> | |
| {#if !$apiKey} | |
| <ApiKeyInput onApiKeySet={handleApiKeySet} /> | |
| {:else if showEngine} | |
| <div class="engine-container"> | |
| {#if errorMessage} | |
| <div class="error-banner"> | |
| <p>❌ {errorMessage}</p> | |
| <button on:click={handleReset} class="retry-button"> | |
| Start Over | |
| </button> | |
| </div> | |
| {/if} | |
| {#if $isGenerating} | |
| <div class="generating-banner"> | |
| <p>🎬 Generating your adventure...</p> | |
| </div> | |
| {/if} | |
| <StoryEngine onError={handleEngineError} /> | |
| <div class="controls"> | |
| <button on:click={handleReset} class="reset-button"> | |
| Restart Adventure | |
| </button> | |
| </div> | |
| </div> | |
| {/if} | |
| </main> | |
| <footer class="app-footer"> | |
| <p>Powered by OpenAI's GPT-4 and Sora</p> | |
| </footer> | |
| </div> | |
| <style> | |
| .app { | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; | |
| background: linear-gradient(to bottom, #f8f9fa, #e9ecef); | |
| } | |
| .app-header { | |
| text-align: center; | |
| padding: 2rem 1rem 1rem; | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| color: white; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| } | |
| .app-header h1 { | |
| margin: 0; | |
| font-size: 2.5rem; | |
| font-weight: 700; | |
| text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); | |
| } | |
| .subtitle { | |
| margin: 0.5rem 0 0; | |
| font-size: 1.1rem; | |
| opacity: 0.95; | |
| } | |
| main { | |
| flex: 1; | |
| max-width: 1400px; | |
| width: 100%; | |
| margin: 0 auto; | |
| padding: 2rem 1rem; | |
| } | |
| .engine-container { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 1rem; | |
| } | |
| .error-banner, | |
| .generating-banner { | |
| padding: 1rem 1.5rem; | |
| border-radius: 0.5rem; | |
| text-align: center; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 1rem; | |
| } | |
| .error-banner { | |
| background: #ffebee; | |
| color: #c62828; | |
| border: 1px solid #ffcdd2; | |
| } | |
| .generating-banner { | |
| background: #e3f2fd; | |
| color: #1565c0; | |
| border: 1px solid #bbdefb; | |
| } | |
| .error-banner p, | |
| .generating-banner p { | |
| margin: 0; | |
| font-weight: 600; | |
| } | |
| .retry-button { | |
| padding: 0.5rem 1rem; | |
| background: white; | |
| color: #c62828; | |
| border: 1px solid #ffcdd2; | |
| border-radius: 0.375rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: background 0.2s; | |
| } | |
| .retry-button:hover { | |
| background: #fff5f5; | |
| } | |
| .controls { | |
| display: flex; | |
| justify-content: center; | |
| padding: 2rem 0; | |
| } | |
| .reset-button { | |
| padding: 0.75rem 1.5rem; | |
| background: #6c757d; | |
| color: white; | |
| border: none; | |
| border-radius: 0.5rem; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: background 0.2s; | |
| } | |
| .reset-button:hover { | |
| background: #5a6268; | |
| } | |
| .app-footer { | |
| text-align: center; | |
| padding: 2rem 1rem; | |
| background: white; | |
| border-top: 1px solid #dee2e6; | |
| color: #6c757d; | |
| font-size: 0.9rem; | |
| } | |
| .app-footer p { | |
| margin: 0; | |
| } | |
| @media (max-width: 768px) { | |
| .app-header h1 { | |
| font-size: 2rem; | |
| } | |
| .subtitle { | |
| font-size: 1rem; | |
| } | |
| main { | |
| padding: 1rem 0.5rem; | |
| } | |
| } | |
| </style> |