Labo Prompt Director https://cdn.tailwindcss.com tailwind.config = {
// --- PROTECTION WORDPRESS ---
// On désactive le 'preflight' pour ne pas casser le style de votre thème
corePlugins: {
preflight: false,
},
theme: {
extend: {
colors: {
labo: {
DEFAULT: '#39FF14', // Vert Fluo Signature
600: '#1DE01C', // Hover plus sombre
},
neutral: {
900: '#111827',
}
},
fontFamily: {
sans: ['Inter', 'sans-serif'],
},
boxShadow: {
'labo-focus': '0 0 0 4px rgba(57, 255, 20, 0.25)', // L'aura vert fluo
'soft': '0 4px 6px -1px rgba(0, 0, 0, 0.05), 0 2px 4px -1px rgba(0, 0, 0, 0.03)',
}
}
}
}
/* --- ISOLATION ET PROTECTION CSS --- */
/* On applique les resets nécessaires UNIQUEMENT dans notre wrapper */
#labo-tool-wrapper {
font-family: 'Inter', sans-serif;
color: #111827;
background-color: #F3F4F6;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
} /* Restaurer les bordures par défaut de Tailwind uniquement pour l'outil
(car sans preflight, les bordures disparaissent) */
#labo-tool-wrapper *,
#labo-tool-wrapper ::before,
#labo-tool-wrapper ::after {
box-sizing: border-box;
border-width: 0;
border-style: solid;
border-color: #e5e7eb;
} /* Restaurer les styles par défaut pour les images dans l'outil */
#labo-tool-wrapper img {
border-style: none;
max-width: 100%;
height: auto;
display: block;
}
/* Restaurer les styles de boutons de base */
#labo-tool-wrapper button {
background-color: transparent;
background-image: none;
cursor: pointer;
} /* --- RECETTE VISUELLE CSS --- */ /* Cartes */
.section-card {
background-color: #FFFFFF;
border: 1px solid #E5E7EB;
border-width: 1px; /* Force border width */
border-radius: 1rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.02);
overflow: hidden;
transition: box-shadow 0.3s ease;
}
.section-card:hover {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.05);
} .card-header {
background-color: #F9FAFB;
border-bottom: 1px solid #F3F4F6;
border-bottom-width: 1px;
padding: 1rem 1.25rem;
display: flex;
align-items: center;
gap: 0.5rem;
} .card-body {
padding: 1.25rem;
} /* Accent Titre */
.title-accent {
display: inline-block;
position: relative;
}
.title-accent::after {
content: '';
position: absolute;
bottom: -4px;
left: 0;
width: 20px;
height: 3px;
background-color: #39FF14;
border-radius: 2px;
} /* Champs de Formulaire (.form-control) */
.form-control {
width: 100%;
border: 1px solid #D1D5DB;
border-width: 1px;
border-radius: 0.75rem;
padding: 0.6rem 0.75rem;
font-size: 0.875rem;
background-color: #FFFFFF;
transition: all 0.2s ease-in-out;
outline: none;
color: #111827; /* Force text color */
} .form-control:focus {
border-color: #39FF14;
box-shadow: 0 0 0 4px rgba(57, 255, 20, 0.25);
} .select-wrapper {
position: relative;
}
.select-icon {
position: absolute;
right: 0.75rem;
top: 50%;
transform: translateY(-50%);
pointer-events: none;
color: #9CA3AF;
} /* Boutons */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
border-radius: 0.75rem;
font-weight: 600;
font-size: 0.875rem;
transition: all 0.2s;
cursor: pointer;
text-decoration: none; /* WordPress override */
} .btn-primary {
background-color: #39FF14;
color: #0A0A0A;
border: 1px solid #32E012;
border-width: 1px;
box-shadow: 0 2px 4px rgba(57, 255, 20, 0.2);
}
.btn-primary:hover {
background-color: #1DE01C;
transform: translateY(-1px);
}
.btn-primary:active {
transform: translateY(0);
} .btn-ghost {
background-color: transparent;
border: 1px solid #E5E7EB;
border-width: 1px;
color: #4B5563;
background-color: #FFFFFF;
}
.btn-ghost:hover {
background-color: #F9FAFB;
border-color: #D1D5DB;
color: #111827;
} /* Labels & Textes */
.field-label {
display: block;
font-size: 0.75rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #6B7280;
margin-bottom: 0.35rem;
} .help-text {
font-size: 0.75rem;
color: #9CA3AF;
margin-top: 0.25rem;
} /* Toast Notification */
#toast-container {
position: fixed;
bottom: 1.5rem;
right: 1.5rem;
z-index: 9999; /* Higher z-index for WP admin bar conflict */
pointer-events: none;
}
.toast {
background: white;
padding: 1rem 1.5rem;
border-radius: 0.75rem;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
gap: 0.75rem;
transform: translateY(20px);
opacity: 0;
transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
border-left: 4px solid #39FF14;
}
.toast.show {
transform: translateY(0);
opacity: 1;
} /* Animation Pulse */
@keyframes pulse-glow {
0% { box-shadow: 0 0 0 0 rgba(57, 255, 20, 0.7); }
70% { box-shadow: 0 0 0 10px rgba(57, 255, 20, 0); }
100% { box-shadow: 0 0 0 0 rgba(57, 255, 20, 0); }
}
.btn-highlight-pulse {
animation: pulse-glow 2s infinite;
position: relative;
z-index: 10;
}
// --- DATA ---
const SUBJECT_SUGGESTIONS = [
'Un homme en pull noir, portrait buste, expression concentrée',
'Une femme en blazer beige, portrait buste, regard confiant',
'Un conseiller en insertion dans un bureau, posture professionnelle',
'Une personne au look casual, portrait naturel, léger sourire',
'Un duo en entretien, conseiller + bénéficiaire, ambiance respectueuse',
'Un groupe en atelier, participants attentifs, scène réaliste',
'Un portrait serré, visage expressif, émotions contenues',
'Un profil côté, yeux fermés, mood ciné',
'Une personne tenant un carnet, posture studieuse',
'Un candidat en tenue simple, prêt pour un entretien',
'Une scène “avant/après” (stress vs confiance) en diptyque',
'Un formateur face caméra, style éditorial',
'Une personne sur fond urbain, style street réaliste',
'Un portrait studio minimaliste, fond neutre',
'Une personne en extérieur, lumière douce, naturel',
'Un gros plan mains + objet (carnet/stylo), rendu réaliste',
'Un portrait avec accessoires discrets (lunettes, montre)',
'Une personne devant un ordinateur portable, concentration',
'Un portrait trois-quarts, expression déterminée',
'Une personne en tenue sombre, ambiance premium',
'Une personne en t-shirt, style simple et crédible',
'Un portrait mi-corps, posture ouverte, attitude bienveillante',
]; const ACTION_SUGGESTIONS = [
'Regard caméra, léger sourire, posture droite, mains hors champ',
'Regard hors champ, air réfléchi, respiration calme',
'Bras croisés, expression confiante mais accessible',
'Assis à un bureau, prise de notes, concentration',
'Main sur le menton, pose analytique, regard précis',
'En train de pointer un document, posture d’explication',
'Tête légèrement inclinée, écoute active, bienveillance',
'Rire discret, détente, naturel',
'Debout, posture stable, épaule relâchée, assurance',
'Regard vers la lumière, expression inspirée',
'Main tenant un stylo, geste suspendu, attention',
'Mains visibles mais propres, gestes naturels, anatomie correcte',
'Marche légère, instantané “sur le vif”',
'Regard sérieux, bouche neutre, posture pro',
'Expression surprise maîtrisée, sourcils relevés',
'Sourire franc, posture ouverte, énergie positive',
'Profil, yeux fermés, mood ciné, souffle visible',
'Assis, épaules légèrement en avant, implication',
'Léger mouvement de cheveux, brise douce, naturel',
'Interaction avec un objet (carnet, laptop), focus',
'Regard vers l’écran, reflet léger dans les yeux',
'Pose “stop-scroll” : cadrage proche, micro-émotion lisible',
]; const ENVIRONMENT_SUGGESTIONS = [
'Bureau lumineux, ordinateur portable flou en arrière-plan',
'Salle de réunion, tableau blanc discret, ambiance pro',
'Espace coworking, lumière naturelle, arrière-plan doux',
'Fond neutre studio, texture légère, minimaliste',
'Couloir d’entreprise, profondeur, bokeh propre',
'Extérieur urbain, rue calme, arrière-plan flouté',
'Café calme, lumière douce, ambiance chaleureuse',
'Bibliothèque, livres flous, mood studieux',
'Atelier collectif, chaises, paperboard, scène réaliste',
'Bureau CIP : post-its, dossiers, décoration sobre',
'Fenêtre en arrière-plan, lumière diffuse, ombres douces',
'Studio photo, fond gris, éclairage contrôlé',
'Maison intérieure, ambiance simple et crédible',
'Parc en hiver, tons froids, naturel',
'Rue au coucher de soleil, golden hour, bokeh',
'Décor industriel léger, textures métal/béton, propre',
'Salle de formation, tables, ordinateur, contexte pédagogique',
'Bureau minimal, plante verte, atmosphère zen',
'Open space, lignes de fuite, profondeur',
'Fond sombre premium, touches de lumière, ambiance luxe',
'Intérieur vintage, textures chaudes, caractère',
'Mur texturé crème, ambiance nostalgique',
]; const MOOD_SUGGESTIONS = [
'Calme, confiance, sobre, crédible',
'Énergie positive, dynamique, chaleureux',
'Sérieux, déterminé, professionnel',
'Bienveillant, à l’écoute, humain',
'Ciné dramatique, émotion contenue',
'Authentique, spontané, documentaire',
'Premium, luxe, très propre',
'Inspirant, optimiste, lumineux',
'Concentration, focus, rigueur',
'Détente maîtrisée, naturel',
'Tension légère (avant entretien), réaliste',
'Soulagement (après réussite), discret',
'Curiosité, ouverture, engagement',
'Simplicité, minimalisme, modernité',
'Chaleur humaine, proximité',
'Ambiance froide d’hiver, calme',
'Golden hour, douceur, nostalgie',
'Ambiance néon urbaine, moderne',
'Corporate soft (pas froid), crédible',
'Éditorial magazine, propre',
'Stop-scroll : contraste fort, lisible mobile',
'Studio photo net, sérieux',
]; const FRAMING_OPTIONS = [
{ value: 'extreme close-up', label: 'Gros plan extrême (Yeux/Détail)' },
{ value: 'close-up', label: 'Gros plan (Visage)' },
{ value: 'medium shot', label: 'Plan poitrine (Buste)' },
{ value: 'cowboy shot', label: 'Plan américain (Mi-cuisses)' },
{ value: 'full body', label: 'Plan pied (Entier)' },
{ value: 'wide shot', label: 'Plan large (Décor visible)' },
]; const LIGHTING_OPTIONS = [
{ value: 'soft natural lighting', label: 'Lumière naturelle douce' },
{ value: 'cinematic lighting', label: 'Éclairage cinéma (contraste)' },
{ value: 'studio lighting', label: 'Éclairage studio (propre)' },
{ value: 'rembrandt lighting', label: 'Lumière Rembrandt (dramatique)' },
{ value: 'golden hour', label: 'Heure dorée (chaud)' },
{ value: 'neon lighting', label: 'Néons (moderne/tech)' },
]; const STYLE_OPTIONS = [
{ value: 'photorealistic, 8k, highly detailed', label: 'Photo Réaliste (Standard)' },
{ value: 'cinematic still, color graded', label: 'Cinéma (Film)' },
{ value: 'editorial photography, magazine style', label: 'Éditorial / Magazine' },
{ value: 'corporate portrait, professional headshot', label: 'Corporate / LinkedIn' },
{ value: 'analog film, grain, vintage', label: 'Argentique (Vintage)' },
]; const RATIOS = [
{ label: 'Wide (16:9)', value: 'Wide aspect ratio (16:9)' },
{ label: 'Square (1:1)', value: 'Square aspect ratio (1:1)' },
{ label: 'Vertical (9:16)', value: 'Vertical aspect ratio (9:16)' },
{ label: 'Portrait (4:5)', value: 'Portrait aspect ratio (4:5)' },
{ label: 'Ultrawide (21:9)', value: 'Cinematic Ultrawide ratio (21:9)' },
]; // NEW: Options pour l'optique
const CAMERA_OPTIONS = [
{ label: 'Standard (50mm, f/1.8) - Naturel', value: '50mm lens, f/1.8, natural depth of field' },
{ label: 'Portrait (85mm, f/1.4) - Flou d’arrière-plan', value: '85mm lens, f/1.4, strong bokeh' },
{ label: 'Grand Angle (24mm, f/2.8) - Scène large', value: '24mm wide angle lens, f/2.8' },
{ label: 'Macro (100mm) - Détail extrême', value: '100mm macro lens, extreme detail' },
{ label: 'Cinéma (35mm) - Reportage/Street', value: '35mm lens, cinematic look' },
{ label: 'Téléobjectif (200mm) - Compression', value: '200mm telephoto lens, background compression' },
{ label: 'GoPro / Fisheye - Immersif', value: 'Fisheye lens, distorted wide angle' },
{ label: 'Tilt-Shift - Effet Miniature', value: 'Tilt-shift lens, miniature effect' }
]; const DEFAULT_SYSTEM = `Tu es un expert DALL-E 3 pour la création d'images pédagogiques et professionnelles.
Ta mission : Transformer l'intention utilisateur en un prompt riche, descriptif et narratif que DALL-E 3 comprendra parfaitement. Règles d'or pour DALL-E 3 :
1. Pas de listes de mots-clés : Utilise des phrases descriptives complètes.
2. Structure : Décris le SUJET (qui il est, ce qu'il porte), l'ACTION (précise), l'ENVIRONNEMENT (arrière-plan, éclairage) et le STYLE (technique photo).
3. Anglais : Rédige le prompt final en anglais pour une meilleure qualité d'image.
4. Négatif : DALL-E gère moins bien les prompts négatifs explicites, concentre-toi sur ce que tu veux voir.
5. Cohérence : Assure-toi que l'image reste "Clean", "Corporate" mais "Humaine" (style Labo des CIP).`; // --- INIT ---
document.addEventListener('DOMContentLoaded', () => {
populateSelects();
generate(); // Initial generation
}); function populateSelects() {
fillSelect('select-subject', SUBJECT_SUGGESTIONS);
fillSelect('select-action', ACTION_SUGGESTIONS);
fillSelect('select-environment', ENVIRONMENT_SUGGESTIONS);
fillSelect('select-mood', MOOD_SUGGESTIONS);
fillSelectObjects('select-framing', FRAMING_OPTIONS, 'medium shot');
fillSelectObjects('select-lighting', LIGHTING_OPTIONS, 'soft natural lighting');
fillSelectObjects('select-style', STYLE_OPTIONS, 'photorealistic, 8k, highly detailed');
fillSelectObjects('select-ratio', RATIOS, 'Wide aspect ratio (16:9)');
// Populate Camera Options but with empty default to respect manual input
fillSelectObjects('select-camera', CAMERA_OPTIONS, '');
} function fillSelect(id, array) {
const select = document.getElementById(id);
array.forEach(item => {
const opt = document.createElement('option');
opt.value = item;
opt.textContent = item.length > 50 ? item.substring(0, 50) + '...' : item;
select.appendChild(opt);
});
} function fillSelectObjects(id, array, defaultValue) {
const select = document.getElementById(id);
array.forEach(item => {
const opt = document.createElement('option');
opt.value = item.value;
opt.textContent = item.label;
// Only select if it matches default and isn't the Camera select (which we want neutral or handled differently)
if(defaultValue && item.value === defaultValue) opt.selected = true;
select.appendChild(opt);
});
} // --- LOGIC --- function updateInput(type, value) {
if(!value) return;
document.getElementById(`input-${type}`).value = value;
generate();
} function resetAll() {
document.getElementById('input-subject').value = '';
document.getElementById('input-action').value = '';
document.getElementById('input-environment').value = '';
document.getElementById('input-mood').value = '';
// Reset Camera to default
document.getElementById('input-camera').value = '85mm lens, f/1.8';
// Reset Checkbox to TRUE (Checked by default)
const checkbox = document.getElementById('check-reference');
if (checkbox) checkbox.checked = true; // Reset dropdowns to first option (placeholder)
['subject', 'action', 'environment', 'mood', 'camera'].forEach(id => {
const el = document.getElementById(`select-${id}`);
if(el) el.selectedIndex = 0;
}); generate();
showToast('Réinitialisation effectuée', 'success');
} function translateStub(text) {
if (!text) return '';
return text.replace('Un homme', 'A man')
.replace('Une femme', 'A woman')
.replace('Un bureau', 'An office')
.replace('lumière', 'light');
} function generate() {
// Get Values
const subject = document.getElementById('input-subject').value;
const action = document.getElementById('input-action').value;
const environment = document.getElementById('input-environment').value;
const mood = document.getElementById('input-mood').value;
const useReference = document.getElementById('check-reference').checked; const framing = document.getElementById('select-framing').value;
const lighting = document.getElementById('select-lighting').value;
const style = document.getElementById('select-style').value;
const ratio = document.getElementById('select-ratio').value;
const camera = document.getElementById('input-camera').value;
const refText = "Use the uploaded reference image as the primary facial and identity reference. Preserve 100% of the man’s facial structure, proportions, skin texture, age, haircut, and expression accuracy. No stylization or facial alteration."; // 1. Intention
const intentionText = `${subject || 'Un sujet'} ${action || 'faisant une action'}, dans ${environment || 'un lieu'}, ambiance ${mood || 'neutre'}.`;
document.getElementById('display-intention').textContent = intentionText; // 2. Mock Prompt Construction (Simulated Natural Language for DALL-E)
const basePrompt = `${useReference ? refText + ' ' : ''}Create a ${style} image of ${translateStub(subject)} ${translateStub(action)}, located in ${translateStub(environment)}. The mood is ${translateStub(mood)}. Shot type: ${framing} with ${lighting}. Camera: ${camera}. Aspect Ratio: ${ratio}.`;
document.getElementById('hidden-prompt-only').value = basePrompt; // 3. Full Output - Now implicitly includes DEFAULT_SYSTEM
const output = `${DEFAULT_SYSTEM} ## USER REQUEST (DALL-E 3)
${useReference ? `🚨 **PHOTO DE RÉFÉRENCE FOURNIE**
Instruction prioritaire à inclure au début de chaque prompt :
> "${refText}"
` : ''}
Voici mon intention créative pour une image DALL-E 3 :
"${intentionText}" Paramètres techniques souhaités :
- Cadrage : ${framing}
- Éclairage : ${lighting}
- Style : ${style}
- Format : ${ratio}
- Optique : ${camera} Tâche :
1. **Analyse et Renforce** mon intention : "Muscle" le prompt pour garantir une image haute définition, impactante et professionnelle. Ajoute des détails sensoriels (textures, atmosphère) si nécessaire.
2. Génère **4 Variantes** en anglais :
- **Variantes A, B, C** : Scrupuleusement fidèles à ma demande, descriptives et précises.
- **Variante D (La Surprise Créative)** : Prends des libertés artistiques ! Propose un angle de vue inattendu, une composition audacieuse ou une interprétation conceptuelle pour un effet "Waouh", tout en gardant le sujet principal.`; document.getElementById('full-output').value = output;
} // --- UTILS --- function copyToClipboard(elementId) {
const el = document.getElementById(elementId);
// Robust copy method for iframes
if (navigator.userAgent.match(/ipad|iphone/i)) {
// iOS specific
const range = document.createRange();
range.selectNodeContents(el);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
el.setSelectionRange(0, 999999);
} else {
el.select();
}
try {
document.execCommand('copy');
showToast('Copié avec succès !', 'success');
} catch (err) {
console.error('Erreur copie', err);
showToast('Erreur lors de la copie', 'error');
}
} function showToast(message, type = 'success') {
const container = document.getElementById('toast-container');
const toast = document.createElement('div');
toast.className = 'toast';
toast.innerHTML = `
${message} `;
if (type === 'error') {
toast.style.borderLeftColor = '#EF4444';
toast.style.backgroundColor = '#FEF2F2';
} container.appendChild(toast); // Trigger animation
requestAnimationFrame(() => {
toast.classList.add('show');
}); setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => {
container.removeChild(toast);
}, 300);
}, 3000);
}