memory / index.html
Diquels's picture
Add 2 files
993c8b7 verified
<!DOCTYPE html>
<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" },
// Google
{ 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>