Hello Editions

Générateur d'article — Hello Éditions

Générateur d'article

Hello Éditions
⚙ Paramètres WordPress
Guide pas à pas — comment utiliser le générateur

Étape 1 — Remplir les métadonnées
Choisis la catégorie, la date, le temps de lecture, l'auteur et l'URL slug (l'URL courte de l'article, ex. parcours-manuscrit). Les champs marqués d'une * sont obligatoires pour générer le prompt.

Étape 2 — Saisir le titre et le chapô
Écris le titre (obligatoire) et optionnellement le chapô (1 à 2 phrases qui donnent envie de lire). Ces éléments guident Claude pour la rédaction.

Étape 3 — Ajouter les infos à inclure (optionnel)
Liste les faits, chiffres, angles ou consignes que Claude doit faire apparaître dans l'article (ex. « 85 % de nos publications viennent de manuscrits spontanés »).

Étape 4 — Générer le prompt pour Claude
Clique sur « 1. Générer le prompt pour Claude ». Le prompt apparaît à droite, prêt à être copié (bouton Copier le prompt) et collé dans une conversation Claude.

Étape 5 — Importer la réponse de Claude
Claude renvoie un brief (JSON avec titre, chapô, corps en Markdown, articles liés…). Clique sur « 2. Importer un brief », colle la réponse complète, valide. Tous les champs se remplissent automatiquement.

Étape 6 — Ajouter l'image à la une (optionnel)
Remplis l'URL de l'image, le texte alternatif (alt) et la légende si nécessaire.

Étape 7 — Régénérer et prévisualiser
Clique sur « Régénérer le HTML ». L'aperçu s'affiche à droite. Tu peux revenir aux champs pour ajuster et régénérer autant de fois que nécessaire.

Étape 8 — Publier
Deux options :
Copier le HTML puis le coller dans un article WordPress (éditeur HTML).
Créer un brouillon WP : configure une fois l'URL de ton site + identifiants d'application WordPress, puis le brouillon est créé en un clic dans ton admin.

Rappel syntaxe Markdown du corps : ## Titre, ### Sous-titre, **gras**, *italique*, [lien](/url), - item, 1. item, > citation, --- pour un séparateur. Le 1er paragraphe reçoit automatiquement le style « intro ».

Métadonnées
En-tête
Infos à inclure dans l'article (optionnel, utilisé dans le prompt Claude)
Image à la une (optionnel)
Corps de l'article (Markdown)
Articles liés (rempli automatiquement par l'import du brief)

Articles publiés sur le site

Liste récupérée automatiquement depuis ton WordPress. Elle se met à jour à chaque publication.

Chargement…
${html}`); doc.close(); } // ============================================ // Parse d'un brief (frontmatter YAML-like + Markdown) // ============================================ function parseBrief(text) { if (!text || !text.trim()) throw new Error('Le brief est vide.'); const lines = text.split(/\r?\n/); const meta = {}; let bodyStart = 0; // Détection du frontmatter if (lines[0] && lines[0].trim() === '---') { let frontmatterEnd = -1; for (let i = 1; i < lines.length; i++) { if (lines[i].trim() === '---') { frontmatterEnd = i; break; } // key: value (peut contenir ':' dans la valeur) const match = lines[i].match(/^([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(.*)$/); if (match) { meta[match[1].trim()] = match[2].trim(); } } if (frontmatterEnd === -1) { throw new Error('Frontmatter non fermé — ajoute "---" à la fin des métadonnées.'); } bodyStart = frontmatterEnd + 1; } const body = lines.slice(bodyStart).join('\n').trim(); return { meta, body }; } // ============================================ // Applique un brief aux champs du formulaire // ============================================ function applyBrief(brief) { const m = brief.meta; const setVal = (id, val) => { if (val !== undefined && val !== '') { document.getElementById(id).value = val; } }; // Mapping catégorie (slug) if (m.categorie) { const sel = document.getElementById('f-slug'); const validSlugs = Array.from(sel.options).map(o => o.value); if (validSlugs.includes(m.categorie)) { sel.value = m.categorie; } } setVal('f-date', m.date); setVal('f-temps', m.temps); setVal('f-auteur', m.auteur); setVal('f-titre', m.titre); setVal('f-chapeau', m.chapeau); setVal('f-img-url', m.image_url); setVal('f-img-alt', m.image_alt); setVal('f-img-legende', m.image_legende); setVal('f-slug-url', m.slug); setVal('f-related', m.related); if (brief.body) { document.getElementById('f-contenu').value = brief.body; } generate(); } // ============================================ // Génération du prompt pour Claude // ============================================ function generatePrompt() { const slug = document.getElementById('f-slug').value; const catNom = CATEGORIES[slug] || slug; const dateIso = document.getElementById('f-date').value; const titre = document.getElementById('f-titre').value.trim(); const chapeau = document.getElementById('f-chapeau').value.trim(); const slugUrl = document.getElementById('f-slug-url').value.trim(); const infos = document.getElementById('f-infos').value.trim(); // Validation des champs obligatoires const missing = []; if (!titre) missing.push('Titre de l\'article'); if (!slugUrl) missing.push('URL slug'); if (missing.length > 0) { alert('Pour générer le prompt, remplis d\'abord :\n\n• ' + missing.join('\n• ') + '\n\n(Les champs obligatoires sont marqués d\'un * orange.)'); if (!titre) document.getElementById('f-titre').focus(); else document.getElementById('f-slug-url').focus(); return; } const archiveText = (FETCH_STATE === 'ok' && PUBLISHED_ARTICLES.length > 0) ? exportArchiveText() : 'Aucun article publié pour le moment — ne propose pas de liens internes ni d\'articles liés.'; const sujet = titre || '[sujet à remplir — mets-le dans le champ Titre]'; const chapeauBlock = chapeau ? `ANGLE / CHAPÔ SOUHAITÉ :\n${chapeau}\n\n` : ''; const infosBlock = infos ? `INFOS À FAIRE APPARAÎTRE DANS L'ARTICLE (obligatoires) :\n${infos}\n\n` : ''; const prompt = `Tu es rédacteur pour Hello Éditions, maison d'édition française. Tu dois écrire un article pour notre rubrique « Conseils aux auteurs », destinée aux auteurs qui cherchent à publier leur manuscrit. SUJET DE L'ARTICLE : ${sujet} CATÉGORIE : ${slug} (${catNom}) DATE DE PUBLICATION : ${dateIso || '[à définir]'} SLUG URL SOUHAITÉ : ${slugUrl || '[à définir, kebab-case, sans accent]'} ${chapeauBlock}${infosBlock}${archiveText} INSTRUCTIONS DE RÉDACTION : - Ton : expert, bienveillant, rassurant pour un primo-auteur - Longueur : entre 900 et 1500 mots - Structure : chapô de 2 phrases max + un paragraphe d'intro + 3 à 5 sections en H2 + sous-sections H3 si pertinent + mini-conclusion pratique - Intègre 2 à 4 liens internes vers les articles publiés listés ci-dessus quand c'est pertinent (ancre naturelle dans le corps du texte). Si la liste est vide, n'en mets aucun. - Dans le champ « related » du frontmatter, propose 2 à 3 slugs d'articles publiés complémentaires (vide si aucun article publié) FORMAT DE SORTIE ATTENDU (YAML frontmatter + corps Markdown, rien d'autre) : --- titre: ${titre || '[titre accrocheur, 60 caractères max]'} categorie: ${slug} date: ${dateIso || '[YYYY-MM-DD]'} temps: [nombre de minutes de lecture estimé] auteur: Hello Éditions chapeau: ${chapeau || '[1 à 2 phrases qui donnent envie de lire]'} slug: ${slugUrl || '[slug-url-en-kebab-case]'} image_url: image_alt: image_legende: related: [slug1, slug2] --- [corps de l'article en Markdown : paragraphes, ## H2, ### H3, - listes, > citations] Rédige maintenant l'article complet en respectant ce format exact.`; const ta = document.getElementById('output-prompt'); ta.value = prompt; // Basculer sur l'onglet Prompt document.querySelectorAll('.output__tab').forEach(t => t.classList.remove('is-active')); document.querySelectorAll('.output__pane').forEach(p => p.classList.remove('is-active')); document.querySelector('.output__tab[data-pane="prompt"]').classList.add('is-active'); document.getElementById('pane-prompt').classList.add('is-active'); } // ============================================ // ARTICLES PUBLIÉS — lecture depuis l'API WordPress // ============================================ // URL de base de l'API. Le chemin relatif marche si le générateur // est hébergé sur le même domaine que WordPress. const WP_API_BASE = (window.location.hostname && window.location.hostname.endsWith('helloeditions.fr')) ? '/wp-json/wp/v2' : 'https://helloeditions.fr/wp-json/wp/v2'; // Cache local des articles récupérés depuis WP (pas de persistence) let PUBLISHED_ARTICLES = []; let FETCH_STATE = 'idle'; // 'idle' | 'loading' | 'error' | 'ok' function stripHtml(html) { const tmp = document.createElement('div'); tmp.innerHTML = html || ''; return (tmp.textContent || tmp.innerText || '').trim(); } // Compat : certains endroits du code appellent encore loadArchive() function loadArchive() { return PUBLISHED_ARTICLES; } async function fetchPublishedArticles() { FETCH_STATE = 'loading'; renderArchive(); try { const url = `${WP_API_BASE}/posts?per_page=100&_embed=1&orderby=date&order=desc&status=publish`; const res = await fetch(url, { credentials: 'same-origin' }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const posts = await res.json(); PUBLISHED_ARTICLES = posts.map(p => { const terms = (p._embedded && p._embedded['wp:term']) || []; const cats = terms[0] || []; const mainCat = cats[0]; // Si le slug de catégorie WP matche un de nos slugs connus, on le garde tel quel const catSlug = mainCat ? mainCat.slug : ''; return { slug: p.slug, titre: stripHtml((p.title && p.title.rendered) || ''), categorie: catSlug, categorieNom: mainCat ? mainCat.name : '', date: p.date ? p.date.split('T')[0] : '', chapeau: stripHtml((p.excerpt && p.excerpt.rendered) || ''), link: p.link || '' }; }); FETCH_STATE = 'ok'; } catch (e) { console.error('Erreur fetch WP:', e); FETCH_STATE = 'error'; PUBLISHED_ARTICLES = []; } renderArchive(); } function renderArchive() { const list = document.getElementById('archive-list'); const count = document.getElementById('archive-count'); if (!list || !count) return; if (FETCH_STATE === 'loading') { count.textContent = '…'; list.innerHTML = '
Chargement depuis WordPress…
'; updateRelatedStatus(); return; } if (FETCH_STATE === 'error') { count.textContent = 'erreur'; list.innerHTML = `
Impossible de récupérer les articles depuis WordPress.
Vérifie que ${WP_API_BASE}/posts est accessible.
`; updateRelatedStatus(); return; } const archive = PUBLISHED_ARTICLES; count.textContent = archive.length + (archive.length <= 1 ? ' article' : ' articles'); if (archive.length === 0) { list.innerHTML = '
Aucun article publié pour le moment.
Les articles apparaîtront ici dès leur publication sur le site.
'; updateRelatedStatus(); return; } list.innerHTML = archive.map((item, idx) => `
${escapeHtml(CATEGORIES[item.categorie] || item.categorieNom || item.categorie || '—')}

${escapeHtml(item.titre || '')}

${escapeHtml(item.slug || '')} ${escapeHtml(item.date || '')}
`).join(''); // Event delegation list.querySelectorAll('.archive__item-btn').forEach(btn => { btn.addEventListener('click', () => { const idx = parseInt(btn.dataset.idx, 10); const action = btn.dataset.action; if (action === 'use') { addSlugToRelated(PUBLISHED_ARTICLES[idx].slug); } }); }); updateRelatedStatus(); } function addSlugToRelated(slug) { const field = document.getElementById('f-related'); const current = field.value.split(',').map(s => s.trim()).filter(Boolean); if (current.includes(slug)) return; current.push(slug); field.value = current.join(', '); generate(); } function updateRelatedStatus() { const field = document.getElementById('f-related'); const status = document.getElementById('f-related-status'); if (!field || !status) return; const slugs = field.value.split(',').map(s => s.trim()).filter(Boolean); if (slugs.length === 0) { status.textContent = ''; status.style.color = 'var(--muted)'; return; } const archiveSlugs = PUBLISHED_ARTICLES.map(a => a.slug); const found = slugs.filter(s => archiveSlugs.includes(s)); const missing = slugs.filter(s => !archiveSlugs.includes(s)); if (missing.length === 0) { status.textContent = `✓ ${found.length} article(s) trouvé(s) parmi les articles publiés.`; status.style.color = '#0d8f3e'; } else { status.textContent = `⚠ Non publié(s) : ${missing.join(', ')}`; status.style.color = '#c23c3c'; } } function exportArchiveText() { if (FETCH_STATE === 'loading') return 'Chargement en cours…'; if (FETCH_STATE === 'error') return 'Impossible de récupérer les articles depuis WordPress.'; if (PUBLISHED_ARTICLES.length === 0) return 'Aucun article publié pour le moment.'; const lines = PUBLISHED_ARTICLES.map(a => { const catNom = CATEGORIES[a.categorie] || a.categorieNom || a.categorie || '—'; return `- [${a.categorie || '—'}] /conseils/${a.slug}/ — "${a.titre}" (${a.date})${a.chapeau ? '\n Chapô : ' + a.chapeau : ''}`; }); return `ARTICLES PUBLIÉS SUR HELLOEDITIONS.FR (${PUBLISHED_ARTICLES.length}) :\n\n${lines.join('\n')}`; } // ============================================ // CONFIG WORDPRESS (Application Password) // ============================================ const WP_CONFIG_KEY = 'he_wp_config_v1'; function loadWpConfig() { try { const raw = localStorage.getItem(WP_CONFIG_KEY); if (!raw) return null; const cfg = JSON.parse(raw); if (cfg && cfg.url && cfg.user && cfg.pass) return cfg; return null; } catch (e) { return null; } } function saveWpConfig(cfg) { localStorage.setItem(WP_CONFIG_KEY, JSON.stringify(cfg)); } function clearWpConfig() { localStorage.removeItem(WP_CONFIG_KEY); } function openWpModal() { const cfg = loadWpConfig() || {}; document.getElementById('wp-url').value = cfg.url || `https://${(window.location.hostname || 'helloeditions.fr')}`; document.getElementById('wp-user').value = cfg.user || ''; document.getElementById('wp-pass').value = cfg.pass || ''; document.getElementById('wp-error').classList.remove('is-visible'); document.getElementById('wp-modal').classList.add('is-open'); } function closeWpModal() { document.getElementById('wp-modal').classList.remove('is-open'); } // ============================================ // CRÉATION DE BROUILLON WORDPRESS // ============================================ async function createWpDraft() { const btn = document.getElementById('btn-create-wp-draft'); const cfg = loadWpConfig(); if (!cfg) { openWpModal(); return; } const titre = document.getElementById('f-titre').value.trim(); const slugUrl = document.getElementById('f-slug-url').value.trim(); const catSlug = document.getElementById('f-slug').value; const dateIso = document.getElementById('f-date').value; const html = document.getElementById('output-html').value; if (!titre) { alert('Titre manquant. Remplis-le avant de créer le brouillon.'); return; } if (!slugUrl) { alert('Slug URL manquant. Remplis-le avant de créer le brouillon.'); return; } if (!html.trim()) { alert('Aucun HTML généré. Clique sur "Régénérer le HTML" d\'abord.'); return; } const baseUrl = cfg.url.replace(/\/$/, ''); const authHeader = 'Basic ' + btoa(cfg.user + ':' + cfg.pass); btn.classList.add('is-loading'); btn.classList.remove('is-success', 'is-error'); const originalText = btn.textContent; btn.textContent = 'Création…'; try { // 1. Récupérer l'ID de la catégorie depuis son slug const catRes = await fetch(`${baseUrl}/wp-json/wp/v2/categories?slug=${encodeURIComponent(catSlug)}`, { headers: { 'Authorization': authHeader } }); if (!catRes.ok) throw new Error(`Erreur récupération catégorie (HTTP ${catRes.status})`); const cats = await catRes.json(); if (!cats.length) throw new Error(`Catégorie "${catSlug}" introuvable sur WordPress. Crée-la d'abord.`); const catId = cats[0].id; // 2. Créer le brouillon const body = { title: titre, slug: slugUrl, content: html, status: 'draft', categories: [catId] }; if (dateIso) body.date = dateIso + 'T09:00:00'; const postRes = await fetch(`${baseUrl}/wp-json/wp/v2/posts`, { method: 'POST', headers: { 'Authorization': authHeader, 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); if (!postRes.ok) { const errText = await postRes.text(); throw new Error(`HTTP ${postRes.status} — ${errText.substring(0, 200)}`); } const post = await postRes.json(); btn.classList.remove('is-loading'); btn.classList.add('is-success'); btn.textContent = '✓ Brouillon créé !'; const editUrl = `${baseUrl}/wp-admin/post.php?post=${post.id}&action=edit`; setTimeout(() => { if (confirm(`Brouillon créé avec succès (ID ${post.id}).\n\nOuvrir dans WordPress maintenant ?`)) { window.open(editUrl, '_blank'); } }, 200); setTimeout(() => { btn.classList.remove('is-success'); btn.textContent = originalText; }, 4000); } catch (err) { console.error(err); btn.classList.remove('is-loading'); btn.classList.add('is-error'); btn.textContent = '✗ Échec'; alert('Erreur lors de la création du brouillon :\n\n' + err.message + '\n\nVérifie tes identifiants dans ⚙ Paramètres WordPress.'); setTimeout(() => { btn.classList.remove('is-error'); btn.textContent = originalText; }, 3000); } } // ============================================ // Init + events // ============================================ function init() { // Date d'aujourd'hui par défaut const today = new Date().toISOString().split('T')[0]; document.getElementById('f-date').value = today; document.getElementById('btn-generate').addEventListener('click', generate); document.getElementById('btn-generate-prompt').addEventListener('click', generatePrompt); document.getElementById('btn-create-wp-draft').addEventListener('click', createWpDraft); document.getElementById('btn-copy-prompt').addEventListener('click', () => { const ta = document.getElementById('output-prompt'); if (!ta.value) { generatePrompt(); } ta.select(); navigator.clipboard.writeText(ta.value).then(() => { const btn = document.getElementById('btn-copy-prompt'); const original = btn.textContent; btn.textContent = 'Copié !'; btn.classList.add('is-copied'); setTimeout(() => { btn.textContent = original; btn.classList.remove('is-copied'); }, 2000); }); }); document.getElementById('btn-reset').addEventListener('click', () => { if (!confirm('Effacer tous les champs ?')) return; document.querySelectorAll('.form__input, .form__textarea').forEach(el => { if (el.id === 'f-auteur') el.value = 'Hello Éditions'; else if (el.id === 'f-temps') el.value = '5'; else el.value = ''; }); document.getElementById('f-slug').selectedIndex = 0; document.getElementById('f-date').value = today; document.getElementById('output-html').value = ''; generate(); }); document.getElementById('btn-copy').addEventListener('click', () => { const ta = document.getElementById('output-html'); ta.select(); navigator.clipboard.writeText(ta.value).then(() => { const btn = document.getElementById('btn-copy'); const original = btn.textContent; btn.textContent = 'Copié !'; btn.classList.add('is-copied'); setTimeout(() => { btn.textContent = original; btn.classList.remove('is-copied'); }, 2000); }); }); // Switch tabs document.querySelectorAll('.output__tab').forEach(tab => { tab.addEventListener('click', () => { document.querySelectorAll('.output__tab').forEach(t => t.classList.remove('is-active')); document.querySelectorAll('.output__pane').forEach(p => p.classList.remove('is-active')); tab.classList.add('is-active'); document.getElementById('pane-' + tab.dataset.pane).classList.add('is-active'); }); }); // Auto-regen on blur of any field document.querySelectorAll('.form__input, .form__select, .form__textarea').forEach(el => { el.addEventListener('change', generate); }); document.getElementById('f-related').addEventListener('input', updateRelatedStatus); // ===== ARTICLES PUBLIÉS (depuis WP) ===== fetchPublishedArticles(); document.getElementById('btn-archive-refresh').addEventListener('click', () => { fetchPublishedArticles(); }); document.getElementById('btn-archive-export-text').addEventListener('click', () => { const pane = document.getElementById('archive-export-pane'); const ta = document.getElementById('archive-export-ta'); ta.value = exportArchiveText(); pane.classList.add('is-visible'); ta.select(); }); document.getElementById('btn-archive-copy').addEventListener('click', () => { const ta = document.getElementById('archive-export-ta'); ta.select(); navigator.clipboard.writeText(ta.value).then(() => { const btn = document.getElementById('btn-archive-copy'); const o = btn.textContent; btn.textContent = 'Copié !'; setTimeout(() => btn.textContent = o, 1500); }); }); // ===== Modale Import ===== const modal = document.getElementById('import-modal'); const modalTextarea = document.getElementById('import-textarea'); const modalError = document.getElementById('import-error'); const openModal = () => { modal.classList.add('is-open'); modalError.classList.remove('is-visible'); setTimeout(() => modalTextarea.focus(), 50); }; const closeModal = () => { modal.classList.remove('is-open'); modalError.classList.remove('is-visible'); }; document.getElementById('btn-import').addEventListener('click', openModal); document.getElementById('btn-modal-cancel').addEventListener('click', closeModal); document.getElementById('btn-modal-import').addEventListener('click', () => { try { const brief = parseBrief(modalTextarea.value); applyBrief(brief); modalTextarea.value = ''; closeModal(); } catch (err) { modalError.textContent = err.message; modalError.classList.add('is-visible'); } }); // Fermer avec Échap / clic sur le fond modal.addEventListener('click', (e) => { if (e.target === modal) closeModal(); }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && modal.classList.contains('is-open')) closeModal(); }); // ===== Modale Config WordPress ===== const wpModal = document.getElementById('wp-modal'); document.getElementById('btn-wp-settings').addEventListener('click', (e) => { e.preventDefault(); openWpModal(); }); document.getElementById('btn-wp-cancel').addEventListener('click', closeWpModal); document.getElementById('btn-wp-clear').addEventListener('click', () => { if (!confirm('Effacer les identifiants WordPress enregistrés ?')) return; clearWpConfig(); document.getElementById('wp-url').value = ''; document.getElementById('wp-user').value = ''; document.getElementById('wp-pass').value = ''; }); document.getElementById('btn-wp-save').addEventListener('click', () => { const url = document.getElementById('wp-url').value.trim(); const user = document.getElementById('wp-user').value.trim(); const pass = document.getElementById('wp-pass').value.trim(); const err = document.getElementById('wp-error'); if (!url || !user || !pass) { err.textContent = 'Les 3 champs sont obligatoires.'; err.classList.add('is-visible'); return; } saveWpConfig({ url, user, pass }); closeWpModal(); }); wpModal.addEventListener('click', (e) => { if (e.target === wpModal) closeWpModal(); }); document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && wpModal.classList.contains('is-open')) closeWpModal(); }); // Génération initiale generate(); } document.addEventListener('DOMContentLoaded', init);
Panier Fermer
📚 Déposez votre manuscrit →