fix: batch priorités — thème, scroll, wizard, agents, slides, charts, packs
Some checks failed
CI / Lint, Unit Tests & Build (push) Failing after 1m15s
CI / Deploy production (on server) (push) Has been skipped

Anti-flash dark (color-scheme + applyDocumentTheme), scroll pages publiques,
prompts wizard/slides moins génériques, cron agents rattrapage nextRun null,
slides carnet via notebookId, graphiques à 2 crédits + hint, audit dashboard,
packs Stripe marqués non configurés sans price ID.
This commit is contained in:
Antigravity
2026-07-16 20:48:55 +00:00
parent 556a0b2f3f
commit a77f3ffb6e
16 changed files with 240 additions and 39 deletions

View File

@@ -42,7 +42,8 @@ const LANG_NAMES: Record<string, string> = {
function wordTargetsForLevel(level?: string): { min: number; max: number; label: string } {
const l = (level || '').toLowerCase()
if (/expert|avancé|advanced|fort/.test(l)) {
return { min: 400, max: 800, label: 'expert (400800 words per note)' }
// Borné pour éviter timeouts JSON / mode expert qui plantait
return { min: 350, max: 700, label: 'expert (350700 words per note, dense)' }
}
if (/intermédiaire|intermediate|moyen/.test(l)) {
return { min: 300, max: 550, label: 'intermediate (300550 words per note)' }
@@ -240,7 +241,13 @@ Return ONLY:
Rules:
- Exactly ${count} titles, progressive learning order.
- Titles in ${this.langName(lang)} only.
- notebookName ≠ full user sentence.`
- notebookName ≠ full user sentence (38 words, concrete, topic-specific).
- FORBIDDEN generic notebook names / chapter patterns (any language):
"Fondements", "Foundations", "Maîtrise", "Mastery", "Introduction",
"Basics", "Overview", "Getting started", "Chapitre 1", "Chapter 1",
"Partie 1", "Bases", "Notions de base", "Synthèse finale" alone.
- Prefer specific titles that name the real subject (e.g. topic keywords + angle).
- Each title must be unique and non-interchangeable with another topic.`
const raw = await provider.generateText(prompt)
const parsed = this.parseJsonObject(raw)
@@ -250,10 +257,41 @@ Rules:
while (titles.length < count) {
titles.push(lang === 'fr' ? `Chapitre ${titles.length + 1}` : `Chapter ${titles.length + 1}`)
}
return {
notebookName: String(parsed?.notebookName || '').trim().slice(0, 80),
titles,
let notebookName = String(parsed?.notebookName || '').trim().slice(0, 80)
if (this.isGenericNotebookName(notebookName, topic)) {
notebookName = this.deriveNotebookName(topic, titles.map((t) => ({ title: t, content: '' })), lang)
}
const cleanedTitles = titles.map((t, i) =>
this.isGenericChapterTitle(t) ? this.fallbackChapterTitle(topic, i, lang) : t,
)
return {
notebookName,
titles: cleanedTitles,
}
}
private isGenericNotebookName(name: string, topic: string): boolean {
const n = (name || '').trim().toLowerCase()
if (!n || n.length < 3) return true
if (n === topic.trim().toLowerCase() && topic.trim().split(/\s+/).length > 10) return true
const banned =
/^(fondements?|foundations?|ma[iî]trise|mastery|introduction|bases?|basics?|overview|getting started|notions de base|synth[eè]se|summary)$/i
return banned.test(n)
}
private isGenericChapterTitle(title: string): boolean {
const t = (title || '').trim()
if (!t) return true
return /^(chapitre|chapter|partie|part|section)\s*\d+$/i.test(t)
|| /^(fondements?|foundations?|ma[iî]trise|introduction|bases?|basics?|overview)$/i.test(t)
}
private fallbackChapterTitle(topic: string, index: number, lang: string): string {
const short = topic.trim().slice(0, 48) || (lang === 'fr' ? 'Sujet' : 'Topic')
const anglesFr = ['Concepts clés', 'Méthodes', 'Exemples guidés', 'Pièges fréquents', 'Mise en pratique', 'Bilan']
const anglesEn = ['Key concepts', 'Methods', 'Guided examples', 'Common pitfalls', 'Practice', 'Recap']
const angles = lang === 'fr' ? anglesFr : anglesEn
return `${angles[index % angles.length]}${short}`
}
private async generateOneNote(