Spaces:
Sleeping
Sleeping
| <html lang="fr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>AI Flashcards Generator</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.6.2/axios.min.js"></script> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" rel="stylesheet"> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap'); | |
| body { | |
| font-family: 'Space Grotesk', sans-serif; | |
| background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%); | |
| } | |
| .glass-morph { | |
| background: rgba(255, 255, 255, 0.05); | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .card-hover { | |
| transition: all 0.3s ease; | |
| } | |
| .card-hover:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 20px 40px rgba(0, 0, 0, 0.2); | |
| } | |
| .gradient-text { | |
| background: linear-gradient(45deg, #60a5fa, #a855f7); | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| color: transparent; | |
| } | |
| .custom-loader { | |
| width: 50px; | |
| height: 50px; | |
| border: 3px solid #fff; | |
| border-radius: 50%; | |
| display: inline-block; | |
| position: relative; | |
| box-sizing: border-box; | |
| animation: rotation 1s linear infinite; | |
| } | |
| .custom-loader::after { | |
| content: ''; | |
| box-sizing: border-box; | |
| position: absolute; | |
| left: 50%; | |
| top: 50%; | |
| transform: translate(-50%, -50%); | |
| width: 40px; | |
| height: 40px; | |
| border-radius: 50%; | |
| border: 3px solid transparent; | |
| border-bottom-color: #60a5fa; | |
| } | |
| @keyframes rotation { | |
| 0% { transform: rotate(0deg) } | |
| 100% { transform: rotate(360deg) } | |
| } | |
| .fade-enter-active, .fade-leave-active { | |
| transition: opacity 0.5s ease; | |
| } | |
| .fade-enter-from, .fade-leave-to { | |
| opacity: 0; | |
| } | |
| </style> | |
| </head> | |
| <body class="min-h-screen text-gray-100"> | |
| <div id="app" class="container mx-auto px-4 py-12"> | |
| <!-- Hero Section --> | |
| <div class="text-center mb-16 animate__animated animate__fadeIn"> | |
| <h1 class="text-5xl font-bold mb-4 gradient-text">AI Flashcards Generator</h1> | |
| <p class="text-xl text-gray-400 mb-8">Transformez vos sujets en cartes d'apprentissage intelligentes</p> | |
| </div> | |
| <!-- Main Content --> | |
| <div class="max-w-4xl mx-auto"> | |
| <!-- Input Section --> | |
| <div class="glass-morph rounded-2xl p-8 mb-12 card-hover"> | |
| <div class="mb-6"> | |
| <label for="topic" class="block text-lg font-medium mb-3 text-gray-300">Quel sujet souhaitez-vous explorer ?</label> | |
| <div class="relative"> | |
| <input | |
| type="text" | |
| id="topic" | |
| v-model="topic" | |
| class="w-full px-6 py-4 bg-gray-800/50 rounded-xl border border-gray-700 focus:ring-2 focus:ring-blue-500 focus:border-transparent text-lg transition-all duration-300" | |
| placeholder="Ex: Intelligence Artificielle, Quantum Computing..." | |
| :disabled="isLoading" | |
| > | |
| </div> | |
| </div> | |
| <button | |
| @click="generateFlashcards" | |
| :disabled="isLoading" | |
| class="w-full bg-gradient-to-r from-blue-500 to-purple-600 text-white py-4 px-8 rounded-xl font-medium text-lg hover:opacity-90 transition-all duration-300 flex items-center justify-center space-x-3" | |
| > | |
| <span v-if="!isLoading">Générer les Flashcards</span> | |
| <span v-else class="custom-loader"></span> | |
| </button> | |
| </div> | |
| <!-- Error Message --> | |
| <transition name="fade"> | |
| <div v-if="error" class="mb-8 animate__animated animate__shakeX"> | |
| <div class="bg-red-500/20 border border-red-500/50 text-red-300 px-6 py-4 rounded-xl"> | |
| [[error]] | |
| </div> | |
| </div> | |
| </transition> | |
| <!-- Results Section --> | |
| <transition name="fade"> | |
| <div v-if="flashcards.length > 0" class="glass-morph rounded-2xl overflow-hidden"> | |
| <!-- Tabs --> | |
| <div class="border-b border-gray-700/50"> | |
| <div class="flex"> | |
| <button | |
| v-for="tab in ['study', 'json']" | |
| :key="tab" | |
| @click="activeTab = tab" | |
| :class="[ | |
| 'px-8 py-4 font-medium text-lg transition-all duration-300', | |
| activeTab === tab | |
| ? 'gradient-text border-b-2 border-blue-500' | |
| : 'text-gray-400 hover:text-gray-300' | |
| ]" | |
| > | |
| [[tab === 'study' ? 'Mode Étude' : 'Mode JSON']] | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Tab Content --> | |
| <div class="p-6"> | |
| <!-- Study Mode --> | |
| <div v-if="activeTab === 'study'" class="space-y-6"> | |
| <div | |
| v-for="(card, index) in flashcards" | |
| :key="index" | |
| class="glass-morph rounded-xl p-6 card-hover cursor-pointer transition-all duration-300" | |
| @click="card.showAnswer = !card.showAnswer" | |
| > | |
| <div class="flex items-start space-x-4"> | |
| <div class="w-12 h-12 flex items-center justify-center rounded-full bg-blue-500/20 text-blue-400 font-bold"> | |
| [[index + 1]] | |
| </div> | |
| <div class="flex-1"> | |
| <h3 class="text-xl font-medium mb-4">[[card.question]]</h3> | |
| <transition name="fade"> | |
| <div v-if="card.showAnswer" class="mt-4 pt-4 border-t border-gray-700/50"> | |
| <p class="text-gray-300">[[card.answer]]</p> | |
| </div> | |
| </transition> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- JSON Mode --> | |
| <div v-if="activeTab === 'json'" class="bg-gray-800/50 rounded-xl p-6 overflow-x-auto"> | |
| <pre class="text-gray-300">[[JSON.stringify(flashcards, null, 2)]]</pre> | |
| </div> | |
| </div> | |
| </div> | |
| </transition> | |
| </div> | |
| </div> | |
| <script> | |
| const { createApp } = Vue | |
| createApp({ | |
| delimiters: ['[[', ']]'], | |
| data() { | |
| return { | |
| topic: '', | |
| flashcards: [], | |
| activeTab: 'study', | |
| isLoading: false, | |
| error: null | |
| } | |
| }, | |
| methods: { | |
| async generateFlashcards() { | |
| if (!this.topic) { | |
| this.error = 'Veuillez entrer un sujet.' | |
| return | |
| } | |
| this.isLoading = true | |
| this.error = null | |
| this.flashcards = [] | |
| try { | |
| const response = await axios.post('/generate', { | |
| topic: this.topic | |
| }) | |
| if (response.data.success) { | |
| const cards = response.data.flashcards.map(card => ({ | |
| ...card, | |
| showAnswer: false | |
| })) | |
| // Animation des cartes à leur apparition | |
| setTimeout(() => { | |
| this.flashcards = cards | |
| this.$nextTick(() => { | |
| gsap.from('.card-hover', { | |
| y: 30, | |
| opacity: 0, | |
| duration: 0.5, | |
| stagger: 0.1 | |
| }) | |
| }) | |
| }, 300) | |
| } | |
| } catch (error) { | |
| this.error = error.response?.data?.error || 'Une erreur est survenue lors de la génération.' | |
| } finally { | |
| this.isLoading = false | |
| } | |
| } | |
| }, | |
| mounted() { | |
| // Animation du titre à l'entrée | |
| gsap.from('.gradient-text', { | |
| y: -50, | |
| opacity: 0, | |
| duration: 1, | |
| ease: 'power3.out' | |
| }) | |
| } | |
| }).mount('#app') | |
| </script> | |
| </body> | |
| </html> |