feat: publication IA (magazine/brief/essay) + fixes critique
Publication IA: - 4 templates (magazine, brief, essay, simple) avec CSS riche - Rewrite IA (article/exercises/tutorial/reference/mixed) - Modération avec timeout 12s + fallback safe - Quotas publish_enhance par tier (basic=2, pro=15, business=100) - Détection contenu stale (hash) - Migration DB publishedContent/publishedTemplate/publishedSourceHash Fixes: - cheerio v1.2: Element -> AnyNode (domhandler), decodeEntities cast - _isShared ajouté au type Note (champ virtuel serveur) - callout colors PDF export: extraction fonction pure testable - admin/published: guard note.userId null - Cmd+S fonctionne en mode dialog (pas seulement fullPage) i18n: - 23 clés publish* traduites dans les 15 locales - Extension Web Clipper: 13 locales mise à jour Tests: - callout-colors.test.ts (6 tests) - note-visible-in-view.test.ts (5 tests) - entitlements.test.ts + byok-entitlements.test.ts: mock usageLog + unstubAllEnvs - 199/199 tests passent Tracker: user-stories.md sync avec sprint-status.yaml
This commit is contained in:
@@ -29,7 +29,7 @@ export class NotebookWizardService {
|
||||
const provider = getChatProvider(config)
|
||||
const raw = await provider.generateText(prompts)
|
||||
|
||||
return this.parseResponse(raw, profile)
|
||||
return this.parseResponse(raw, profile, lang)
|
||||
}
|
||||
|
||||
private buildPrompt(
|
||||
@@ -39,8 +39,9 @@ export class NotebookWizardService {
|
||||
count: number,
|
||||
level?: string
|
||||
): string {
|
||||
const langName = lang === 'fr' ? 'français' : lang === 'en' ? 'English' : lang === 'fa' ? 'فارسی' : lang
|
||||
const langName = lang === 'fr' ? 'français' : lang === 'en' ? 'English' : lang === 'fa' ? 'فارسی' : lang === 'ar' ? 'العربية' : lang === 'de' ? 'Deutsch' : lang === 'es' ? 'Español' : lang === 'it' ? 'Italiano' : lang === 'pt' ? 'Português' : lang === 'ru' ? 'Русский' : lang === 'zh' ? '中文' : lang === 'ja' ? '日本語' : lang === 'ko' ? '한국어' : lang === 'nl' ? 'Nederlands' : lang === 'pl' ? 'Polski' : lang === 'hi' ? 'हिन्दी' : lang
|
||||
|
||||
const schemaLabels = this.getSchemaLabels(lang)
|
||||
const profileContext = {
|
||||
student: `Tu crées des notes de cours pour un étudiant. Le contenu doit être pédagogique, clair, avec des exemples.`,
|
||||
teacher: `Tu crées la structure d'un cours pour un professeur. Chaque note est un chapitre avec des sections à remplir, des objectifs, et des exercices.`,
|
||||
@@ -77,8 +78,8 @@ FORMAT DE SORTIE — JSON UNIQUEMENT :
|
||||
}
|
||||
],
|
||||
"schemaProperties": [
|
||||
{ "name": "Statut", "type": "select", "options": ["À réviser", "En cours", "Maîtrisé"] },
|
||||
{ "name": "Difficulté", "type": "select", "options": ["Facile", "Moyen", "Difficile"] }
|
||||
{ "name": "${schemaLabels.status}", "type": "select", "options": ["${schemaLabels.statusTodo}", "${schemaLabels.statusInProgress}", "${schemaLabels.statusDone}"] },
|
||||
{ "name": "${schemaLabels.difficulty}", "type": "select", "options": ["${schemaLabels.easy}", "${schemaLabels.medium}", "${schemaLabels.hard}"] }
|
||||
]
|
||||
}
|
||||
\`\`\`
|
||||
@@ -110,7 +111,28 @@ IMPORTANT : Chaque note DOIT faire entre 800 et 1500 mots. Sois exhaustif. Déve
|
||||
Les "difficulty" doivent varier : mélange facile/moyen/difficile.`
|
||||
}
|
||||
|
||||
private parseResponse(raw: string, profile: WizardProfile): GeneratedCarnet {
|
||||
private getSchemaLabels(lang: string) {
|
||||
const map: Record<string, { status: string; statusTodo: string; statusInProgress: string; statusDone: string; difficulty: string; easy: string; medium: string; hard: string }> = {
|
||||
fr: { status: 'Statut', statusTodo: 'À réviser', statusInProgress: 'En cours', statusDone: 'Maîtrisé', difficulty: 'Difficulté', easy: 'Facile', medium: 'Moyen', hard: 'Difficile' },
|
||||
en: { status: 'Status', statusTodo: 'To review', statusInProgress: 'In progress', statusDone: 'Mastered', difficulty: 'Difficulty', easy: 'Easy', medium: 'Medium', hard: 'Hard' },
|
||||
fa: { status: 'وضعیت', statusTodo: 'باید مرور شود', statusInProgress: 'در حال یادگیری', statusDone: 'تسلط یافته', difficulty: 'سختی', easy: 'آسان', medium: 'متوسط', hard: 'دشوار' },
|
||||
ar: { status: 'الحالة', statusTodo: 'للمراجعة', statusInProgress: 'قيد الدراسة', statusDone: 'متقن', difficulty: 'الصعوبة', easy: 'سهل', medium: 'متوسط', hard: 'صعب' },
|
||||
de: { status: 'Status', statusTodo: 'Zu wiederholen', statusInProgress: 'In Bearbeitung', statusDone: 'Beherrscht', difficulty: 'Schwierigkeit', easy: 'Einfach', medium: 'Mittel', hard: 'Schwer' },
|
||||
es: { status: 'Estado', statusTodo: 'Por repasar', statusInProgress: 'En progreso', statusDone: 'Dominado', difficulty: 'Dificultad', easy: 'Fácil', medium: 'Medio', hard: 'Difícil' },
|
||||
it: { status: 'Stato', statusTodo: 'Da ripassare', statusInProgress: 'In corso', statusDone: 'Padroneggiato', difficulty: 'Difficoltà', easy: 'Facile', medium: 'Medio', hard: 'Difficile' },
|
||||
pt: { status: 'Estado', statusTodo: 'A rever', statusInProgress: 'Em progresso', statusDone: 'Dominado', difficulty: 'Dificuldade', easy: 'Fácil', medium: 'Médio', hard: 'Difícil' },
|
||||
ru: { status: 'Статус', statusTodo: 'На повторение', statusInProgress: 'В процессе', statusDone: 'Усвоено', difficulty: 'Сложность', easy: 'Лёгкий', medium: 'Средний', hard: 'Сложный' },
|
||||
zh: { status: '状态', statusTodo: '待复习', statusInProgress: '学习中', statusDone: '已掌握', difficulty: '难度', easy: '简单', medium: '中等', hard: '困难' },
|
||||
ja: { status: 'ステータス', statusTodo: '要復習', statusInProgress: '学習中', statusDone: '習得済み', difficulty: '難易度', easy: '易しい', medium: '普通', hard: '難しい' },
|
||||
ko: { status: '상태', statusTodo: '복습 필요', statusInProgress: '학습 중', statusDone: '마스터', difficulty: '난이도', easy: '쉬움', medium: '보통', hard: '어려움' },
|
||||
nl: { status: 'Status', statusTodo: 'Te herhalen', statusInProgress: 'Bezig', statusDone: 'Beheerst', difficulty: 'Moeilijkheid', easy: 'Makkelijk', medium: 'Gemiddeld', hard: 'Moeilijk' },
|
||||
pl: { status: 'Status', statusTodo: 'Do powtórki', statusInProgress: 'W trakcie', statusDone: 'Opanowane', difficulty: 'Trudność', easy: 'Łatwy', medium: 'Średni', hard: 'Trudny' },
|
||||
hi: { status: 'स्थिति', statusTodo: 'दोहराने के लिए', statusInProgress: 'प्रगति में', statusDone: 'महारत हासिल', difficulty: 'कठिनाई', easy: 'आसान', medium: 'मध्यम', hard: 'कठिन' },
|
||||
}
|
||||
return map[lang] || map.en
|
||||
}
|
||||
|
||||
private parseResponse(raw: string, profile: WizardProfile, lang?: string): GeneratedCarnet {
|
||||
const jsonMatch = raw.match(/```json\s*([\s\S]+?)\s*```/)
|
||||
let jsonStr = jsonMatch ? jsonMatch[1] : raw
|
||||
|
||||
@@ -145,11 +167,12 @@ Les "difficulty" doivent varier : mélange facile/moyen/difficile.`
|
||||
})
|
||||
}
|
||||
if (notes.length === 0) throw new Error('No notes found in response')
|
||||
const fallbackLabels = this.getSchemaLabels(lang || 'fr')
|
||||
return {
|
||||
notes,
|
||||
schemaProperties: [
|
||||
{ name: 'Statut', type: 'select', options: ['À réviser', 'En cours', 'Maîtrisé'] },
|
||||
{ name: 'Difficulté', type: 'select', options: ['Facile', 'Moyen', 'Difficile'] },
|
||||
{ name: fallbackLabels.status, type: 'select', options: [fallbackLabels.statusTodo, fallbackLabels.statusInProgress, fallbackLabels.statusDone] },
|
||||
{ name: fallbackLabels.difficulty, type: 'select', options: [fallbackLabels.easy, fallbackLabels.medium, fallbackLabels.hard] },
|
||||
],
|
||||
}
|
||||
} catch {
|
||||
@@ -164,9 +187,10 @@ Les "difficulty" doivent varier : mélange facile/moyen/difficile.`
|
||||
difficulty: n.difficulty || undefined,
|
||||
}))
|
||||
|
||||
const defaultLabels = this.getSchemaLabels(lang || 'fr')
|
||||
const schemaProperties = parsed.schemaProperties || [
|
||||
{ name: 'Statut', type: 'select', options: ['À réviser', 'En cours', 'Maîtrisé'] },
|
||||
{ name: 'Difficulté', type: 'select', options: ['Facile', 'Moyen', 'Difficile'] },
|
||||
{ name: defaultLabels.status, type: 'select', options: [defaultLabels.statusTodo, defaultLabels.statusInProgress, defaultLabels.statusDone] },
|
||||
{ name: defaultLabels.difficulty, type: 'select', options: [defaultLabels.easy, defaultLabels.medium, defaultLabels.hard] },
|
||||
]
|
||||
|
||||
return { notes, schemaProperties }
|
||||
|
||||
Reference in New Issue
Block a user