Download index.html from Diquels/memory: direct link, hf CLI and curl.
- Browser
- Download file 59.8 kB
-
https://huggingface.co/spaces/Diquels/memory/resolve/main/index.html
- Command line
-
hf download hf://spaces/Diquels/memory/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Diquels/memory/resolve/main/index.html
59.8 kB
| <html lang="fr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Prompt Manager</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <script> | |
| tailwind.config = { | |
| theme: { | |
| extend: { | |
| colors: { | |
| pastel: { | |
| pink: '#FFD1DC', | |
| purple: '#E0BBE4', | |
| blue: '#B5EAD7', | |
| yellow: '#FFE5B4', | |
| green: '#C7E9C0', | |
| peach: '#FFC8A2', | |
| lavender: '#D4A5C3', | |
| mint: '#A2D7D8', | |
| }, | |
| border: { | |
| active: '#8B5CF6', // violet plus visible | |
| } | |
| } | |
| } | |
| } | |
| } | |
| </script> | |
| <style> | |
| .prompt-card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 10px 20px rgba(0,0,0,0.1); | |
| } | |
| .tag:hover { | |
| transform: scale(1.05); | |
| } | |
| .category-btn:hover { | |
| transform: scale(1.03); | |
| } | |
| .fade-in { | |
| animation: fadeIn 0.3s ease-in-out; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; } | |
| to { opacity: 1; } | |
| } | |
| .scrollbar-hide::-webkit-scrollbar { | |
| display: none; | |
| } | |
| .star-rating .star { | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| } | |
| .star-rating .star:hover { | |
| transform: scale(1.2); | |
| } | |
| .active-filter { | |
| border: 2px solid #8B5CF6; | |
| box-shadow: 0 0 0 1px rgba(139, 92, 246, 0.3); | |
| } | |
| .prompt-modal-content { | |
| max-height: 80vh; | |
| overflow-y: auto; | |
| } | |
| .ai-select-container { | |
| position: relative; | |
| } | |
| .ai-select-dropdown { | |
| display: none; | |
| position: absolute; | |
| background: white; | |
| border: 1px solid #ddd; | |
| border-radius: 0.5rem; | |
| width: 100%; | |
| z-index: 10; | |
| max-height: 200px; | |
| overflow-y: auto; | |
| } | |
| .ai-select-container.open .ai-select-dropdown { | |
| display: block; | |
| } | |
| .filter-dropdown { | |
| position: relative; | |
| } | |
| .filter-dropdown-btn { | |
| width: 100%; | |
| text-align: left; | |
| } | |
| .filter-dropdown-content { | |
| display: none; | |
| position: absolute; | |
| background-color: white; | |
| min-width: 160px; | |
| box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); | |
| z-index: 1; | |
| border-radius: 0.5rem; | |
| max-height: 300px; | |
| overflow-y: auto; | |
| } | |
| .filter-dropdown:hover .filter-dropdown-content { | |
| display: block; | |
| } | |
| .filter-dropdown-item { | |
| padding: 8px 16px; | |
| cursor: pointer; | |
| } | |
| .filter-dropdown-item:hover { | |
| background-color: #f1f1f1; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-pastel-blue min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <!-- Header --> | |
| <header class="flex justify-between items-center mb-8"> | |
| <div class="flex items-center"> | |
| <i class="fas fa-database text-4xl text-pastel-purple mr-4"></i> | |
| <h1 class="text-4xl font-bold text-gray-700">Prompt Manager</h1> | |
| </div> | |
| <button id="addPromptBtn" class="bg-pastel-pink hover:bg-pastel-lavender text-white px-6 py-3 rounded-full shadow-md transition-all"> | |
| <i class="fas fa-plus mr-2"></i> Nouveau Prompt | |
| </button> | |
| </header> | |
| <!-- Main Content --> | |
| <div class="flex flex-col lg:flex-row gap-8"> | |
| <!-- Sidebar --> | |
| <div class="w-full lg:w-1/4 bg-white rounded-2xl p-6 shadow-lg"> | |
| <div class="mb-8"> | |
| <h2 class="text-xl font-semibold text-gray-700 mb-4">Catégories</h2> | |
| <div class="space-y-2"> | |
| <button class="category-btn w-full text-left px-4 py-2 rounded-lg bg-pastel-green hover:bg-pastel-mint transition-all" data-category="all"> | |
| <i class="fas fa-list mr-2"></i> Tous les prompts | |
| </button> | |
| <button class="category-btn w-full text-left px-4 py-2 rounded-lg bg-pastel-yellow hover:bg-pastel-peach transition-all" data-category="creative"> | |
| <i class="fas fa-paint-brush mr-2"></i> Créatif | |
| </button> | |
| <button class="category-btn w-full text-left px-4 py-2 rounded-lg bg-pastel-purple hover:bg-pastel-lavender transition-all" data-category="technical"> | |
| <i class="fas fa-code mr-2"></i> Technique | |
| </button> | |
| <button class="category-btn w-full text-left px-4 py-2 rounded-lg bg-pastel-peach hover:bg-pastel-pink transition-all" data-category="business"> | |
| <i class="fas fa-briefcase mr-2"></i> Business | |
| </button> | |
| </div> | |
| </div> | |
| <div class="mb-8"> | |
| <h2 class="text-xl font-semibold text-gray-700 mb-4">Trier par</h2> | |
| <div class="space-y-2"> | |
| <button class="sort-btn w-full text-left px-4 py-2 rounded-lg bg-pastel-blue hover:bg-pastel-mint transition-all" data-sort="date-desc"> | |
| <i class="fas fa-clock mr-2"></i> Plus récent | |
| </button> | |
| <button class="sort-btn w-full text-left px-4 py-2 rounded-lg bg-pastel-lavender hover:bg-pastel-pink transition-all" data-sort="rating-desc"> | |
| <i class="fas fa-star mr-2"></i> Meilleures notes | |
| </button> | |
| </div> | |
| </div> | |
| <div> | |
| <h2 class="text-xl font-semibold text-gray-700 mb-4">Filtres</h2> | |
| <div class="mb-4"> | |
| <h3 class="text-sm font-medium text-gray-700 mb-2">IA Optimisée</h3> | |
| <div class="filter-dropdown"> | |
| <button class="filter-dropdown-btn px-4 py-2 bg-gray-100 rounded-lg w-full text-left flex justify-between items-center"> | |
| <span>Sélectionner une IA</span> | |
| <i class="fas fa-chevron-down"></i> | |
| </button> | |
| <div class="filter-dropdown-content"> | |
| <div class="p-2"> | |
| <h4 class="font-medium text-sm mb-1 text-gray-500">OpenAI</h4> | |
| <div class="space-y-1"> | |
| <div class="filter-dropdown-item" data-ai="gpt-4">GPT-4</div> | |
| <div class="filter-dropdown-item" data-ai="gpt-4-turbo">GPT-4 Turbo</div> | |
| <div class="filter-dropdown-item" data-ai="gpt-4o">GPT-4o</div> | |
| <div class="filter-dropdown-item" data-ai="gpt-3.5">GPT-3.5</div> | |
| </div> | |
| </div> | |
| <div class="p-2"> | |
| <h4 class="font-medium text-sm mb-1 text-gray-500">Google</h4> | |
| <div class="space-y-1"> | |
| <div class="filter-dropdown-item" data-ai="gemini-pro">Gemini Pro</div> | |
| <div class="filter-dropdown-item" data-ai="gemini-ultra">Gemini Ultra</div> | |
| <div class="filter-dropdown-item" data-ai="bard">Bard</div> | |
| </div> | |
| </div> | |
| <div class="p-2"> | |
| <h4 class="font-medium text-sm mb-1 text-gray-500">Anthropic</h4> | |
| <div class="space-y-1"> | |
| <div class="filter-dropdown-item" data-ai="claude-3-opus">Claude 3 Opus</div> | |
| <div class="filter-dropdown-item" data-ai="claude-3-sonnet">Claude 3 Sonnet</div> | |
| <div class="filter-dropdown-item" data-ai="claude-3-haiku">Claude 3 Haiku</div> | |
| <div class="filter-dropdown-item" data-ai="claude-2">Claude 2</div> | |
| </div> | |
| </div> | |
| <div class="p-2"> | |
| <h4 class="font-medium text-sm mb-1 text-gray-500">Meta</h4> | |
| <div class="space-y-1"> | |
| <div class="filter-dropdown-item" data-ai="llama-3">Llama 3</div> | |
| <div class="filter-dropdown-item" data-ai="llama-2">Llama 2</div> | |
| </div> | |
| </div> | |
| <div class="p-2"> | |
| <h4 class="font-medium text-sm mb-1 text-gray-500">Autres</h4> | |
| <div class="space-y-1"> | |
| <div class="filter-dropdown-item" data-ai="mistral">Mistral</div> | |
| <div class="filter-dropdown-item" data-ai="copilot">Copilot</div> | |
| <div class="filter-dropdown-item" data-ai="perplexity">Perplexity</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="flex flex-wrap gap-2 mt-2" id="selectedAIFilters"></div> | |
| </div> | |
| <div> | |
| <h3 class="text-sm font-medium text-gray-700 mb-2">Tags</h3> | |
| <div class="flex flex-wrap gap-2"> | |
| <span class="tag cursor-pointer px-3 py-1 bg-pastel-mint rounded-full text-sm transition-all" data-tag="ia">IA</span> | |
| <span class="tag cursor-pointer px-3 py-1 bg-pastel-yellow rounded-full text-sm transition-all" data-tag="marketing">Marketing</span> | |
| <span class="tag cursor-pointer px-3 py-1 bg-pastel-pink rounded-full text-sm transition-all" data-tag="copywriting">Copywriting</span> | |
| <span class="tag cursor-pointer px-3 py-1 bg-pastel-green rounded-full text-sm transition-all" data-tag="productivite">Productivité</span> | |
| <span class="tag cursor-pointer px-3 py-1 bg-pastel-purple rounded-full text-sm transition-all" data-tag="design">Design</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Prompt Display Area --> | |
| <div class="w-full lg:w-3/4"> | |
| <!-- View Toggle --> | |
| <div class="flex justify-between items-center mb-6 bg-white p-4 rounded-2xl shadow-lg"> | |
| <h2 class="text-xl font-semibold text-gray-700">Mes Prompts</h2> | |
| <div class="flex space-x-2"> | |
| <button id="listViewBtn" class="p-2 bg-pastel-blue rounded-lg text-gray-700"> | |
| <i class="fas fa-list"></i> | |
| </button> | |
| <button id="gridViewBtn" class="p-2 bg-pastel-green rounded-lg text-gray-700"> | |
| <i class="fas fa-th-large"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Prompts Container --> | |
| <div id="promptsContainer" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> | |
| <!-- Prompts will be loaded here --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Add/Edit Prompt Modal --> | |
| <div id="promptModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> | |
| <div class="bg-white rounded-2xl p-8 w-full max-w-2xl relative"> | |
| <button id="closeModalBtn" class="absolute top-4 right-4 text-gray-500 hover:text-gray-700"> | |
| <i class="fas fa-times text-2xl"></i> | |
| </button> | |
| <h2 class="text-2xl font-bold text-gray-700 mb-6" id="modalTitle">Ajouter un nouveau prompt</h2> | |
| <form id="promptForm" class="space-y-6"> | |
| <input type="hidden" id="promptId"> | |
| <div> | |
| <label for="promptTitle" class="block text-sm font-medium text-gray-700 mb-1">Titre</label> | |
| <input type="text" id="promptTitle" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-pastel-purple focus:border-transparent"> | |
| </div> | |
| <div> | |
| <label for="promptContent" class="block text-sm font-medium text-gray-700 mb-1">Contenu du prompt</label> | |
| <textarea id="promptContent" rows="5" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-pastel-purple focus:border-transparent"></textarea> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Catégorie</label> | |
| <div class="flex flex-wrap gap-2"> | |
| <label class="inline-flex items-center"> | |
| <input type="radio" name="category" value="creative" class="h-4 w-4 text-pastel-yellow focus:ring-pastel-yellow"> | |
| <span class="ml-2 text-gray-700">Créatif</span> | |
| </label> | |
| <label class="inline-flex items-center"> | |
| <input type="radio" name="category" value="technical" class="h-4 w-4 text-pastel-purple focus:ring-pastel-purple"> | |
| <span class="ml-2 text-gray-700">Technique</span> | |
| </label> | |
| <label class="inline-flex items-center"> | |
| <input type="radio" name="category" value="business" class="h-4 w-4 text-pastel-peach focus:ring-pastel-peach"> | |
| <span class="ml-2 text-gray-700">Business</span> | |
| </label> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Optimisé pour</label> | |
| <div class="ai-select-container"> | |
| <div class="flex flex-wrap gap-2" id="selectedAIList"></div> | |
| <div class="relative mt-2"> | |
| <input type="text" id="aiSearch" class="w-full px-4 py-2 border border-gray-300 rounded-lg" placeholder="Rechercher ou ajouter une IA..."> | |
| <div class="ai-select-dropdown" id="aiDropdown"></div> | |
| </div> | |
| <button type="button" id="addNewAI" class="hidden mt-2 text-sm text-pastel-purple hover:underline">Ajouter une nouvelle IA</button> | |
| </div> | |
| </div> | |
| <div> | |
| <label for="promptTags" class="block text-sm font-medium text-gray-700 mb-1">Tags (séparés par des virgules)</label> | |
| <input type="text" id="promptTags" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-pastel-purple focus:border-transparent" placeholder="IA, marketing, productivité"> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-700 mb-1">Note</label> | |
| <div class="star-rating flex" id="editRating"> | |
| <i class="far fa-star star text-2xl text-pastel-yellow" data-rating="1"></i> | |
| <i class="far fa-star star text-2xl text-pastel-yellow" data-rating="2"></i> | |
| <i class="far fa-star star text-2xl text-pastel-yellow" data-rating="3"></i> | |
| <i class="far fa-star star text-2xl text-pastel-yellow" data-rating="4"></i> | |
| <i class="far fa-star star text-2xl text-pastel-yellow" data-rating="5"></i> | |
| </div> | |
| <input type="hidden" id="promptRating" value="0"> | |
| </div> | |
| <div class="flex justify-end space-x-4"> | |
| <button type="button" id="cancelPromptBtn" class="px-6 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-all"> | |
| Annuler | |
| </button> | |
| <button type="submit" class="px-6 py-2 bg-pastel-pink text-white rounded-lg hover:bg-pastel-lavender transition-all"> | |
| Enregistrer | |
| </button> | |
| </div> | |
| </form> | |
| </div> | |
| </div> | |
| <!-- View Prompt Modal --> | |
| <div id="viewPromptModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> | |
| <div class="bg-white rounded-2xl p-8 w-full max-w-4xl relative"> | |
| <button id="closeViewModalBtn" class="absolute top-4 right-4 text-gray-500 hover:text-gray-700"> | |
| <i class="fas fa-times text-2xl"></i> | |
| </button> | |
| <div class="flex justify-between items-center mb-6"> | |
| <h2 class="text-2xl font-bold text-gray-700" id="viewPromptTitle"></h2> | |
| <div class="flex space-x-4"> | |
| <button id="editPromptBtn" class="px-4 py-2 bg-pastel-blue text-white rounded-lg hover:bg-pastel-mint transition-all"> | |
| <i class="fas fa-edit mr-2"></i> Modifier | |
| </button> | |
| <button id="copyPromptBtn" class="px-4 py-2 bg-pastel-green text-white rounded-lg hover:bg-pastel-mint transition-all"> | |
| <i class="fas fa-copy mr-2"></i> Copier | |
| </button> | |
| </div> | |
| </div> | |
| <div class="prompt-modal-content space-y-6"> | |
| <div class="bg-gray-50 p-4 rounded-lg"> | |
| <pre class="whitespace-pre-wrap text-gray-800" id="viewPromptContent"></pre> | |
| </div> | |
| <div> | |
| <h3 class="text-lg font-semibold text-gray-700 mb-2">Détails</h3> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <h4 class="text-sm font-medium text-gray-500">Catégorie</h4> | |
| <p class="text-gray-800" id="viewPromptCategory"></p> | |
| </div> | |
| <div> | |
| <h4 class="text-sm font-medium text-gray-500">Optimisé pour</h4> | |
| <div class="flex flex-wrap gap-2" id="viewPromptAI"></div> | |
| </div> | |
| <div> | |
| <h4 class="text-sm font-medium text-gray-500">Tags</h4> | |
| <div class="flex flex-wrap gap-2" id="viewPromptTags"></div> | |
| </div> | |
| <div> | |
| <h4 class="text-sm font-medium text-gray-500">Note</h4> | |
| <div class="flex" id="viewPromptRating"></div> | |
| </div> | |
| <div> | |
| <h4 class="text-sm font-medium text-gray-500">Date de création</h4> | |
| <p class="text-gray-800" id="viewPromptDate"></p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // ============================================== | |
| // Mémoire Python simulée avec localStorage | |
| // ============================================== | |
| class PromptDatabase { | |
| constructor() { | |
| this.key = 'promptManagerDatabase'; | |
| this.availableAIs = [ | |
| // OpenAI | |
| { id: "gpt-4", name: "GPT-4" }, | |
| { id: "gpt-4-turbo", name: "GPT-4 Turbo" }, | |
| { id: "gpt-4o", name: "GPT-4o" }, | |
| { id: "gpt-3.5", name: "GPT-3.5" }, | |
| { id: "gemini-pro", name: "Gemini Pro" }, | |
| { id: "gemini-ultra", name: "Gemini Ultra" }, | |
| { id: "bard", name: "Bard" }, | |
| // Anthropic | |
| { id: "claude-3-opus", name: "Claude 3 Opus" }, | |
| { id: "claude-3-sonnet", name: "Claude 3 Sonnet" }, | |
| { id: "claude-3-haiku", name: "Claude 3 Haiku" }, | |
| { id: "claude-2", name: "Claude 2" }, | |
| // Meta | |
| { id: "llama-3", name: "Llama 3" }, | |
| { id: "llama-2", name: "Llama 2" }, | |
| // Others | |
| { id: "mistral", name: "Mistral" }, | |
| { id: "copilot", name: "Copilot" }, | |
| { id: "perplexity", name: "Perplexity" } | |
| ]; | |
| // Initialiser avec des données par défaut si la base est vide | |
| if (!this.load()) { | |
| this.prompts = [ | |
| { | |
| id: 1, | |
| title: "Générer des idées de contenu", | |
| content: "Génère 10 idées de contenu originales pour un blog sur [thème]. Les idées doivent être engageantes et adaptées à un public de [démographie].", | |
| category: "creative", | |
| tags: ["marketing", "copywriting"], | |
| ai: ["gpt-4", "gemini-pro"], | |
| createdAt: new Date('2023-05-15'), | |
| rating: 4 | |
| }, | |
| { | |
| id: 2, | |
| title: "Optimiser le code Python", | |
| content: "Analyse ce code Python et suggère des optimisations pour améliorer les performances : [coller le code]", | |
| category: "technical", | |
| tags: ["ia", "productivite"], | |
| ai: ["gpt-4-turbo", "copilot"], | |
| createdAt: new Date('2023-06-20'), | |
| rating: 5 | |
| }, | |
| { | |
| id: 3, | |
| title: "Stratégie de croissance", | |
| content: "Développe une stratégie de croissance en 5 étapes pour une startup dans le domaine de [industrie] avec un budget de [montant].", | |
| category: "business", | |
| tags: ["marketing", "business"], | |
| ai: ["claude-3-sonnet", "gemini-pro"], | |
| createdAt: new Date('2023-07-10'), | |
| rating: 3 | |
| } | |
| ]; | |
| this.save(); | |
| } | |
| } | |
| // Charger depuis localStorage | |
| load() { | |
| const data = localStorage.getItem(this.key); | |
| if (data) { | |
| try { | |
| const parsed = JSON.parse(data); | |
| this.prompts = parsed.prompts.map(p => ({ | |
| ...p, | |
| createdAt: new Date(p.createdAt) | |
| })); | |
| return true; | |
| } catch (e) { | |
| console.error("Erreur de chargement des données", e); | |
| return false; | |
| } | |
| } | |
| return false; | |
| } | |
| // Sauvegarder dans localStorage | |
| save() { | |
| const data = { | |
| prompts: this.prompts, | |
| availableAIs: this.availableAIs | |
| }; | |
| localStorage.setItem(this.key, JSON.stringify(data)); | |
| } | |
| // Ajouter un prompt | |
| addPrompt(prompt) { | |
| prompt.id = this.prompts.length > 0 ? Math.max(...this.prompts.map(p => p.id)) + 1 : 1; | |
| prompt.createdAt = new Date(); | |
| this.prompts.unshift(prompt); | |
| this.save(); | |
| return prompt; | |
| } | |
| // Mettre à jour un prompt | |
| updatePrompt(id, updatedPrompt) { | |
| const index = this.prompts.findIndex(p => p.id === id); | |
| if (index !== -1) { | |
| this.prompts[index] = { | |
| ...this.prompts[index], | |
| ...updatedPrompt | |
| }; | |
| this.save(); | |
| return this.prompts[index]; | |
| } | |
| return null; | |
| } | |
| // Supprimer un prompt | |
| deletePrompt(id) { | |
| const index = this.prompts.findIndex(p => p.id === id); | |
| if (index !== -1) { | |
| this.prompts.splice(index, 1); | |
| this.save(); | |
| return true; | |
| } | |
| return false; | |
| } | |
| // Obtenir tous les prompts | |
| getAllPrompts() { | |
| return [...this.prompts]; | |
| } | |
| // Obtenir un prompt par ID | |
| getPromptById(id) { | |
| return this.prompts.find(p => p.id === id); | |
| } | |
| // Filtrer les prompts | |
| filterPrompts(filterFn) { | |
| return this.prompts.filter(filterFn); | |
| } | |
| // Ajouter une nouvelle IA | |
| addAI(ai) { | |
| if (!this.availableAIs.some(a => a.id === ai.id)) { | |
| this.availableAIs.push(ai); | |
| this.save(); | |
| } | |
| return ai; | |
| } | |
| // Obtenir le nom d'une IA | |
| getAIName(aiId) { | |
| const ai = this.availableAIs.find(a => a.id === aiId); | |
| return ai ? ai.name : aiId; | |
| } | |
| } | |
| // ============================================== | |
| // Application principale | |
| // ============================================== | |
| // Initialiser la base de données | |
| const db = new PromptDatabase(); | |
| // DOM Elements | |
| const promptsContainer = document.getElementById('promptsContainer'); | |
| const addPromptBtn = document.getElementById('addPromptBtn'); | |
| const promptModal = document.getElementById('promptModal'); | |
| const viewPromptModal = document.getElementById('viewPromptModal'); | |
| const closeModalBtn = document.getElementById('closeModalBtn'); | |
| const closeViewModalBtn = document.getElementById('closeViewModalBtn'); | |
| const cancelPromptBtn = document.getElementById('cancelPromptBtn'); | |
| const promptForm = document.getElementById('promptForm'); | |
| const listViewBtn = document.getElementById('listViewBtn'); | |
| const gridViewBtn = document.getElementById('gridViewBtn'); | |
| const categoryBtns = document.querySelectorAll('.category-btn'); | |
| const sortBtns = document.querySelectorAll('.sort-btn'); | |
| const tags = document.querySelectorAll('.tag'); | |
| const modalTitle = document.getElementById('modalTitle'); | |
| const promptIdInput = document.getElementById('promptId'); | |
| const promptTitleInput = document.getElementById('promptTitle'); | |
| const promptContentInput = document.getElementById('promptContent'); | |
| const promptTagsInput = document.getElementById('promptTags'); | |
| const promptRatingInput = document.getElementById('promptRating'); | |
| const editRating = document.getElementById('editRating'); | |
| const selectedAIList = document.getElementById('selectedAIList'); | |
| const aiSearch = document.getElementById('aiSearch'); | |
| const aiDropdown = document.getElementById('aiDropdown'); | |
| const addNewAI = document.getElementById('addNewAI'); | |
| const viewPromptTitle = document.getElementById('viewPromptTitle'); | |
| const viewPromptContent = document.getElementById('viewPromptContent'); | |
| const viewPromptCategory = document.getElementById('viewPromptCategory'); | |
| const viewPromptAI = document.getElementById('viewPromptAI'); | |
| const viewPromptTags = document.getElementById('viewPromptTags'); | |
| const viewPromptRating = document.getElementById('viewPromptRating'); | |
| const viewPromptDate = document.getElementById('viewPromptDate'); | |
| const editPromptBtn = document.getElementById('editPromptBtn'); | |
| const copyPromptBtn = document.getElementById('copyPromptBtn'); | |
| const selectedAIFilters = document.getElementById('selectedAIFilters'); | |
| const filterDropdownItems = document.querySelectorAll('.filter-dropdown-item'); | |
| // Current view state | |
| let currentView = 'grid'; | |
| let currentCategory = 'all'; | |
| let currentTag = null; | |
| let currentAI = null; | |
| let currentSort = 'date-desc'; | |
| let currentPromptId = null; | |
| let selectedAIs = []; | |
| let isEditing = false; | |
| // Initialize the app | |
| function init() { | |
| renderPrompts(); | |
| setupEventListeners(); | |
| updateViewButtons(); | |
| updateActiveFilters(); | |
| renderAIDropdown(); | |
| } | |
| // Set up event listeners | |
| function setupEventListeners() { | |
| // Modal controls | |
| addPromptBtn.addEventListener('click', () => { | |
| isEditing = false; | |
| modalTitle.textContent = "Ajouter un nouveau prompt"; | |
| promptForm.reset(); | |
| selectedAIs = []; | |
| renderSelectedAIs(); | |
| resetRatingStars(editRating); | |
| promptRatingInput.value = "0"; | |
| promptModal.classList.remove('hidden'); | |
| }); | |
| closeModalBtn.addEventListener('click', () => { | |
| promptModal.classList.add('hidden'); | |
| }); | |
| closeViewModalBtn.addEventListener('click', () => { | |
| viewPromptModal.classList.add('hidden'); | |
| }); | |
| cancelPromptBtn.addEventListener('click', () => { | |
| promptModal.classList.add('hidden'); | |
| }); | |
| // Form submission | |
| promptForm.addEventListener('submit', (e) => { | |
| e.preventDefault(); | |
| savePrompt(); | |
| }); | |
| // View toggle | |
| listViewBtn.addEventListener('click', () => { | |
| currentView = 'list'; | |
| renderPrompts(); | |
| updateViewButtons(); | |
| }); | |
| gridViewBtn.addEventListener('click', () => { | |
| currentView = 'grid'; | |
| renderPrompts(); | |
| updateViewButtons(); | |
| }); | |
| // Category filters | |
| categoryBtns.forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| currentCategory = btn.dataset.category; | |
| currentTag = null; | |
| currentAI = null; | |
| renderPrompts(); | |
| updateActiveFilters(); | |
| }); | |
| }); | |
| // Sort buttons | |
| sortBtns.forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| currentSort = btn.dataset.sort; | |
| renderPrompts(); | |
| updateActiveFilters(); | |
| }); | |
| }); | |
| // Tag filters | |
| tags.forEach(tag => { | |
| tag.addEventListener('click', () => { | |
| currentTag = tag.dataset.tag; | |
| currentCategory = 'all'; | |
| currentAI = null; | |
| renderPrompts(); | |
| updateActiveFilters(); | |
| }); | |
| }); | |
| // AI filter dropdown items | |
| filterDropdownItems.forEach(item => { | |
| item.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| currentAI = item.dataset.ai; | |
| currentCategory = 'all'; | |
| currentTag = null; | |
| renderPrompts(); | |
| updateActiveFilters(); | |
| // Add selected AI to filter chips | |
| selectedAIFilters.innerHTML = ''; | |
| if (currentAI) { | |
| const ai = db.availableAIs.find(a => a.id === currentAI) || { id: currentAI, name: currentAI }; | |
| const chip = document.createElement('span'); | |
| chip.className = 'px-3 py-1 bg-pastel-blue rounded-full text-sm flex items-center'; | |
| chip.innerHTML = ` | |
| ${ai.name} | |
| <button class="ml-2 text-xs" data-ai-id="${ai.id}"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| `; | |
| selectedAIFilters.appendChild(chip); | |
| // Add remove event | |
| const removeBtn = chip.querySelector('button'); | |
| removeBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| currentAI = null; | |
| selectedAIFilters.innerHTML = ''; | |
| renderPrompts(); | |
| updateActiveFilters(); | |
| }); | |
| } | |
| }); | |
| }); | |
| // AI selection | |
| aiSearch.addEventListener('input', () => { | |
| renderAIDropdown(); | |
| }); | |
| aiSearch.addEventListener('focus', () => { | |
| document.querySelector('.ai-select-container').classList.add('open'); | |
| renderAIDropdown(); | |
| }); | |
| document.addEventListener('click', (e) => { | |
| if (!e.target.closest('.ai-select-container')) { | |
| document.querySelector('.ai-select-container').classList.remove('open'); | |
| } | |
| }); | |
| // Edit prompt button | |
| editPromptBtn.addEventListener('click', () => { | |
| if (currentPromptId) { | |
| editPrompt(currentPromptId); | |
| } | |
| }); | |
| // Copy prompt button | |
| copyPromptBtn.addEventListener('click', () => { | |
| if (currentPromptId) { | |
| const prompt = db.getPromptById(currentPromptId); | |
| if (prompt) { | |
| navigator.clipboard.writeText(prompt.content) | |
| .then(() => { | |
| // Show copied notification | |
| const copyBtn = copyPromptBtn; | |
| const originalText = copyBtn.innerHTML; | |
| copyBtn.innerHTML = '<i class="fas fa-check mr-2"></i> Copié!'; | |
| setTimeout(() => { | |
| copyBtn.innerHTML = originalText; | |
| }, 2000); | |
| }) | |
| .catch(err => { | |
| console.error('Failed to copy text: ', err); | |
| }); | |
| } | |
| } | |
| }); | |
| // Rating stars in edit modal | |
| setupRatingStars(editRating); | |
| } | |
| // Update active filter buttons with more visible border | |
| function updateActiveFilters() { | |
| // Category buttons | |
| categoryBtns.forEach(btn => { | |
| if (btn.dataset.category === currentCategory) { | |
| btn.classList.add('active-filter'); | |
| } else { | |
| btn.classList.remove('active-filter'); | |
| } | |
| }); | |
| // Sort buttons | |
| sortBtns.forEach(btn => { | |
| if (btn.dataset.sort === currentSort) { | |
| btn.classList.add('active-filter'); | |
| } else { | |
| btn.classList.remove('active-filter'); | |
| } | |
| }); | |
| // Tag filters | |
| tags.forEach(tag => { | |
| if (tag.dataset.tag === currentTag) { | |
| tag.classList.add('active-filter'); | |
| } else { | |
| tag.classList.remove('active-filter'); | |
| } | |
| }); | |
| } | |
| // Update view toggle buttons | |
| function updateViewButtons() { | |
| if (currentView === 'list') { | |
| listViewBtn.classList.add('bg-pastel-purple', 'text-white'); | |
| gridViewBtn.classList.remove('bg-pastel-purple', 'text-white'); | |
| } else { | |
| gridViewBtn.classList.add('bg-pastel-purple', 'text-white'); | |
| listViewBtn.classList.remove('bg-pastel-purple', 'text-white'); | |
| } | |
| } | |
| // Save or update prompt | |
| function savePrompt() { | |
| const title = promptTitleInput.value; | |
| const content = promptContentInput.value; | |
| const category = document.querySelector('input[name="category"]:checked').value; | |
| // Get tags | |
| const tags = promptTagsInput.value | |
| .split(',') | |
| .map(tag => tag.trim()) | |
| .filter(tag => tag.length > 0); | |
| // Get rating | |
| const rating = parseInt(promptRatingInput.value) || 0; | |
| if (isEditing) { | |
| // Update existing prompt | |
| const updatedPrompt = { | |
| title, | |
| content, | |
| category, | |
| tags, | |
| ai: selectedAIs, | |
| rating | |
| }; | |
| db.updatePrompt(currentPromptId, updatedPrompt); | |
| } else { | |
| // Create new prompt | |
| const newPrompt = { | |
| title, | |
| content, | |
| category, | |
| tags, | |
| ai: selectedAIs, | |
| rating | |
| }; | |
| db.addPrompt(newPrompt); | |
| } | |
| renderPrompts(); | |
| promptModal.classList.add('hidden'); | |
| if (isEditing) { | |
| viewPromptModal.classList.add('hidden'); | |
| } | |
| } | |
| // Edit prompt | |
| function editPrompt(id) { | |
| const prompt = db.getPromptById(id); | |
| if (prompt) { | |
| isEditing = true; | |
| currentPromptId = id; | |
| modalTitle.textContent = "Modifier le prompt"; | |
| // Fill form with prompt data | |
| promptIdInput.value = prompt.id; | |
| promptTitleInput.value = prompt.title; | |
| promptContentInput.value = prompt.content; | |
| document.querySelector(`input[name="category"][value="${prompt.category}"]`).checked = true; | |
| promptTagsInput.value = prompt.tags.join(', '); | |
| promptRatingInput.value = prompt.rating; | |
| // Set selected AIs | |
| selectedAIs = [...prompt.ai]; | |
| renderSelectedAIs(); | |
| // Set rating | |
| setRatingStars(editRating, prompt.rating); | |
| promptModal.classList.remove('hidden'); | |
| viewPromptModal.classList.add('hidden'); | |
| } | |
| } | |
| // View prompt details | |
| function viewPrompt(id) { | |
| const prompt = db.getPromptById(id); | |
| if (prompt) { | |
| currentPromptId = id; | |
| // Fill view modal with prompt data | |
| viewPromptTitle.textContent = prompt.title; | |
| viewPromptContent.textContent = prompt.content; | |
| viewPromptCategory.textContent = getCategoryName(prompt.category); | |
| // Display AI | |
| viewPromptAI.innerHTML = ''; | |
| prompt.ai.forEach(ai => { | |
| const aiElement = document.createElement('span'); | |
| aiElement.className = 'px-3 py-1 bg-pastel-blue rounded-full text-sm'; | |
| aiElement.textContent = db.getAIName(ai); | |
| viewPromptAI.appendChild(aiElement); | |
| }); | |
| // Display tags | |
| viewPromptTags.innerHTML = ''; | |
| prompt.tags.forEach(tag => { | |
| const tagElement = document.createElement('span'); | |
| tagElement.className = 'px-3 py-1 bg-pastel-mint rounded-full text-sm'; | |
| tagElement.textContent = tag; | |
| viewPromptTags.appendChild(tagElement); | |
| }); | |
| // Display rating | |
| viewPromptRating.innerHTML = ''; | |
| for (let i = 1; i <= 5; i++) { | |
| const star = document.createElement('i'); | |
| star.className = i <= prompt.rating ? 'fas fa-star text-pastel-yellow' : 'far fa-star text-pastel-yellow'; | |
| viewPromptRating.appendChild(star); | |
| } | |
| // Display date | |
| viewPromptDate.textContent = formatDate(prompt.createdAt); | |
| viewPromptModal.classList.remove('hidden'); | |
| } | |
| } | |
| // Filter prompts based on current filters | |
| function filterPrompts() { | |
| let filteredPrompts = db.getAllPrompts(); | |
| // Category filter | |
| if (currentCategory !== 'all') { | |
| filteredPrompts = filteredPrompts.filter(prompt => prompt.category === currentCategory); | |
| } | |
| // Tag filter | |
| if (currentTag) { | |
| filteredPrompts = filteredPrompts.filter(prompt => prompt.tags.includes(currentTag)); | |
| } | |
| // AI filter | |
| if (currentAI) { | |
| filteredPrompts = filteredPrompts.filter(prompt => prompt.ai.includes(currentAI)); | |
| } | |
| return filteredPrompts; | |
| } | |
| // Sort prompts | |
| function sortPrompts(promptsToSort) { | |
| const sortedPrompts = [...promptsToSort]; | |
| switch(currentSort) { | |
| case 'date-desc': | |
| sortedPrompts.sort((a, b) => b.createdAt - a.createdAt); | |
| break; | |
| case 'rating-desc': | |
| sortedPrompts.sort((a, b) => b.rating - a.rating); | |
| break; | |
| default: | |
| sortedPrompts.sort((a, b) => b.createdAt - a.createdAt); | |
| } | |
| return sortedPrompts; | |
| } | |
| // Render prompts to the DOM | |
| function renderPrompts() { | |
| let filteredPrompts = filterPrompts(); | |
| filteredPrompts = sortPrompts(filteredPrompts); | |
| if (currentView === 'list') { | |
| renderListView(filteredPrompts); | |
| } else { | |
| renderGridView(filteredPrompts); | |
| } | |
| } | |
| // Render prompts in list view | |
| function renderListView(promptsToRender) { | |
| promptsContainer.innerHTML = ''; | |
| promptsContainer.classList.remove('grid-cols-1', 'md:grid-cols-2', 'lg:grid-cols-3'); | |
| promptsContainer.classList.add('space-y-4'); | |
| promptsToRender.forEach(prompt => { | |
| const promptElement = document.createElement('div'); | |
| promptElement.className = 'bg-white rounded-2xl p-6 shadow-md transition-all prompt-card hover:shadow-lg fade-in cursor-pointer'; | |
| promptElement.dataset.id = prompt.id; | |
| promptElement.innerHTML = ` | |
| <div class="flex justify-between items-start mb-4"> | |
| <h3 class="text-xl font-semibold text-gray-800">${prompt.title}</h3> | |
| <div class="flex items-center"> | |
| <button class="copy-btn mr-4 p-2 bg-pastel-green text-white rounded-full hover:bg-pastel-mint transition-all" data-prompt-id="${prompt.id}"> | |
| <i class="fas fa-copy"></i> | |
| </button> | |
| <div class="star-rating" data-prompt-id="${prompt.id}"> | |
| ${renderRatingStars(prompt.rating)} | |
| </div> | |
| </div> | |
| </div> | |
| <p class="text-gray-600 mb-4">${prompt.content}</p> | |
| <div class="flex flex-wrap gap-2 mb-4"> | |
| ${prompt.ai.map(ai => ` | |
| <span class="px-3 py-1 bg-pastel-blue rounded-full text-xs">${db.getAIName(ai)}</span> | |
| `).join('')} | |
| </div> | |
| <div class="flex justify-between items-center"> | |
| <div class="flex flex-wrap gap-2"> | |
| ${prompt.tags.map(tag => ` | |
| <span class="px-3 py-1 bg-pastel-mint rounded-full text-xs">${tag}</span> | |
| `).join('')} | |
| </div> | |
| <span class="text-xs text-gray-500">${formatDate(prompt.createdAt)}</span> | |
| </div> | |
| `; | |
| promptsContainer.appendChild(promptElement); | |
| // Add click event to view prompt | |
| promptElement.addEventListener('click', () => { | |
| viewPrompt(prompt.id); | |
| }); | |
| // Add click event to copy button | |
| const copyBtn = promptElement.querySelector('.copy-btn'); | |
| copyBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| navigator.clipboard.writeText(prompt.content) | |
| .then(() => { | |
| // Show copied notification | |
| const icon = copyBtn.querySelector('i'); | |
| icon.classList.remove('fa-copy'); | |
| icon.classList.add('fa-check'); | |
| setTimeout(() => { | |
| icon.classList.remove('fa-check'); | |
| icon.classList.add('fa-copy'); | |
| }, 2000); | |
| }) | |
| .catch(err => { | |
| console.error('Failed to copy text: ', err); | |
| }); | |
| }); | |
| }); | |
| // Add event listeners to rating stars | |
| setupRatingStars(); | |
| } | |
| // Render prompts in grid view | |
| function renderGridView(promptsToRender) { | |
| promptsContainer.innerHTML = ''; | |
| promptsContainer.classList.remove('space-y-4'); | |
| promptsContainer.classList.add('grid-cols-1', 'md:grid-cols-2', 'lg:grid-cols-3'); | |
| promptsToRender.forEach(prompt => { | |
| const promptElement = document.createElement('div'); | |
| promptElement.className = 'bg-white rounded-2xl p-6 shadow-md transition-all prompt-card hover:shadow-lg fade-in cursor-pointer'; | |
| promptElement.dataset.id = prompt.id; | |
| promptElement.innerHTML = ` | |
| <div class="flex justify-between items-start mb-4"> | |
| <h3 class="text-lg font-semibold text-gray-800">${prompt.title}</h3> | |
| <div class="flex items-center"> | |
| <button class="copy-btn mr-2 p-2 bg-pastel-green text-white rounded-full hover:bg-pastel-mint transition-all" data-prompt-id="${prompt.id}"> | |
| <i class="fas fa-copy text-sm"></i> | |
| </button> | |
| <div class="star-rating" data-prompt-id="${prompt.id}"> | |
| ${renderRatingStars(prompt.rating, true)} | |
| </div> | |
| </div> | |
| </div> | |
| <p class="text-gray-600 mb-4 text-sm line-clamp-3">${prompt.content}</p> | |
| <div class="flex flex-wrap gap-2 mb-4"> | |
| ${prompt.ai.map(ai => ` | |
| <span class="px-2 py-1 bg-pastel-blue rounded-full text-xs">${db.getAIName(ai)}</span> | |
| `).join('')} | |
| </div> | |
| <div class="flex justify-between items-center"> | |
| <div class="flex flex-wrap gap-2"> | |
| ${prompt.tags.map(tag => ` | |
| <span class="px-2 py-1 bg-pastel-mint rounded-full text-xs">${tag}</span> | |
| `).join('')} | |
| </div> | |
| <span class="text-xs text-gray-500">${formatDate(prompt.createdAt)}</span> | |
| </div> | |
| `; | |
| promptsContainer.appendChild(promptElement); | |
| // Add click event to view prompt | |
| promptElement.addEventListener('click', () => { | |
| viewPrompt(prompt.id); | |
| }); | |
| // Add click event to copy button | |
| const copyBtn = promptElement.querySelector('.copy-btn'); | |
| copyBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| navigator.clipboard.writeText(prompt.content) | |
| .then(() => { | |
| // Show copied notification | |
| const icon = copyBtn.querySelector('i'); | |
| icon.classList.remove('fa-copy'); | |
| icon.classList.add('fa-check'); | |
| setTimeout(() => { | |
| icon.classList.remove('fa-check'); | |
| icon.classList.add('fa-copy'); | |
| }, 2000); | |
| }) | |
| .catch(err => { | |
| console.error('Failed to copy text: ', err); | |
| }); | |
| }); | |
| }); | |
| // Add event listeners to rating stars | |
| setupRatingStars(); | |
| } | |
| // Setup rating stars event listeners for a specific container | |
| function setupRatingStars(container = null) { | |
| const containers = container ? [container] : document.querySelectorAll('.star-rating'); | |
| containers.forEach(ratingContainer => { | |
| const promptId = parseInt(ratingContainer.dataset.promptId); | |
| const stars = ratingContainer.querySelectorAll('.star'); | |
| stars.forEach((star, index) => { | |
| star.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| // Update rating in the data | |
| const prompt = db.getPromptById(promptId); | |
| if (prompt) { | |
| prompt.rating = index + 1; | |
| db.updatePrompt(promptId, { rating: prompt.rating }); | |
| renderPrompts(); | |
| } | |
| }); | |
| }); | |
| }); | |
| } | |
| // Set rating stars | |
| function setRatingStars(container, rating) { | |
| container.innerHTML = ''; | |
| for (let i = 1; i <= 5; i++) { | |
| const star = document.createElement('i'); | |
| star.className = i <= rating ? 'fas fa-star star text-pastel-yellow' : 'far fa-star star text-pastel-yellow'; | |
| star.dataset.rating = i; | |
| container.appendChild(star); | |
| } | |
| setupRatingStars(container); | |
| // Update hidden input value | |
| if (container.id === 'editRating') { | |
| promptRatingInput.value = rating; | |
| } | |
| } | |
| // Reset rating stars | |
| function resetRatingStars(container) { | |
| container.innerHTML = ''; | |
| for (let i = 1; i <= 5; i++) { | |
| const star = document.createElement('i'); | |
| star.className = 'far fa-star star text-pastel-yellow'; | |
| star.dataset.rating = i; | |
| container.appendChild(star); | |
| } | |
| setupRatingStars(container); | |
| // Reset hidden input value | |
| if (container.id === 'editRating') { | |
| promptRatingInput.value = "0"; | |
| } | |
| } | |
| // Render rating stars | |
| function renderRatingStars(rating, small = false) { | |
| let stars = ''; | |
| for (let i = 1; i <= 5; i++) { | |
| if (i <= rating) { | |
| stars += `<i class="fas fa-star star ${small ? 'text-sm' : ''} text-pastel-yellow" data-rating="${i}"></i>`; | |
| } else { | |
| stars += `<i class="far fa-star star ${small ? 'text-sm' : ''} text-pastel-yellow" data-rating="${i}"></i>`; | |
| } | |
| } | |
| return stars; | |
| } | |
| // Render AI dropdown | |
| function renderAIDropdown() { | |
| const searchTerm = aiSearch.value.toLowerCase(); | |
| aiDropdown.innerHTML = ''; | |
| // Filter AIs based on search term | |
| const filteredAIs = db.availableAIs.filter(ai => | |
| ai.name.toLowerCase().includes(searchTerm) || | |
| ai.id.toLowerCase().includes(searchTerm) | |
| ); | |
| // Show matching AIs | |
| filteredAIs.forEach(ai => { | |
| if (!selectedAIs.includes(ai.id)) { | |
| const aiElement = document.createElement('div'); | |
| aiElement.className = 'px-4 py-2 hover:bg-gray-100 cursor-pointer'; | |
| aiElement.textContent = ai.name; | |
| aiElement.dataset.aiId = ai.id; | |
| aiElement.addEventListener('click', () => { | |
| if (!selectedAIs.includes(ai.id)) { | |
| selectedAIs.push(ai.id); | |
| renderSelectedAIs(); | |
| renderAIDropdown(); | |
| } | |
| }); | |
| aiDropdown.appendChild(aiElement); | |
| } | |
| }); | |
| // Show "Add new" option if search term doesn't match any existing AI | |
| if (searchTerm && !filteredAIs.some(ai => | |
| ai.name.toLowerCase() === searchTerm || | |
| ai.id.toLowerCase() === searchTerm | |
| )) { | |
| addNewAI.classList.remove('hidden'); | |
| addNewAI.textContent = `Ajouter "${searchTerm}" comme nouvelle IA`; | |
| addNewAI.onclick = () => { | |
| const newAI = { | |
| id: searchTerm.toLowerCase().replace(/\s+/g, '-'), | |
| name: searchTerm | |
| }; | |
| db.addAI(newAI); | |
| selectedAIs.push(newAI.id); | |
| renderSelectedAIs(); | |
| aiSearch.value = ''; | |
| renderAIDropdown(); | |
| addNewAI.classList.add('hidden'); | |
| }; | |
| } else { | |
| addNewAI.classList.add('hidden'); | |
| } | |
| } | |
| // Render selected AIs | |
| function renderSelectedAIs() { | |
| selectedAIList.innerHTML = ''; | |
| selectedAIs.forEach(aiId => { | |
| const ai = db.availableAIs.find(a => a.id === aiId) || { id: aiId, name: aiId }; | |
| const aiElement = document.createElement('span'); | |
| aiElement.className = 'inline-flex items-center px-3 py-1 bg-pastel-blue rounded-full text-sm mb-2'; | |
| aiElement.innerHTML = ` | |
| ${ai.name} | |
| <button class="ml-2 text-xs" data-ai-id="${ai.id}"> | |
| <i class="fas fa-times"></i> | |
| </button> | |
| `; | |
| selectedAIList.appendChild(aiElement); | |
| // Add remove event | |
| const removeBtn = aiElement.querySelector('button'); | |
| removeBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| selectedAIs = selectedAIs.filter(id => id !== ai.id); | |
| renderSelectedAIs(); | |
| renderAIDropdown(); | |
| }); | |
| }); | |
| } | |
| // Get category name from code | |
| function getCategoryName(categoryCode) { | |
| const categories = { | |
| 'creative': 'Créatif', | |
| 'technical': 'Technique', | |
| 'business': 'Business' | |
| }; | |
| return categories[categoryCode] || categoryCode; | |
| } | |
| // Format date | |
| function formatDate(date) { | |
| return date.toLocaleDateString('fr-FR', { | |
| day: '2-digit', | |
| month: '2-digit', | |
| year: 'numeric', | |
| hour: '2-digit', | |
| minute: '2-digit' | |
| }); | |
| } | |
| // Initialize the application | |
| document.addEventListener('DOMContentLoaded', init); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Diquels/memory" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |