fix: chart/diagram fond cohérent avec le thème + slides strictement proportionnels
- slides-renderer: chart et diagram utilisent bg/text/muted du thème (plus de fond #111827 forcé) - slides.tool: prompt ultra-clair (<50 mots = max 3 slides) + cappedSlides.slice(0,8) côté serveur comme filet de sécurité Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -390,10 +390,11 @@ function SlideContent({ slide, index, palette, radius }: { slide: SlideSpec; ind
|
|||||||
// ── CHART ────────────────────────────────────────────────────────────
|
// ── CHART ────────────────────────────────────────────────────────────
|
||||||
case 'chart':
|
case 'chart':
|
||||||
return (
|
return (
|
||||||
<div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', padding: '48px 60px', background: isDark ? palette.bg : '#111827', textAlign: 'left' }}>
|
<div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', padding: '48px 60px', textAlign: 'left' }}>
|
||||||
<h2 style={{ margin: 0, fontSize: 30, fontWeight: 800, letterSpacing: '-0.04em', color: '#fff' }}>{slide.title}</h2>
|
<h2 style={{ margin: 0, fontSize: 30, fontWeight: 800, letterSpacing: '-0.04em', color: text }}>{slide.title}</h2>
|
||||||
{slide.subtitle && <p style={{ margin: '6px 0 0', fontSize: 15, color: 'rgba(255,255,255,0.5)' }}>{slide.subtitle}</p>}
|
{slide.subtitle && <p style={{ margin: '6px 0 0', fontSize: 15, color: muted }}>{slide.subtitle}</p>}
|
||||||
<div style={{ flex: 1, marginTop: 24 }}>
|
<div style={accentBar} />
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
<SlideChart slide={slide} />
|
<SlideChart slide={slide} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -402,11 +403,12 @@ function SlideContent({ slide, index, palette, radius }: { slide: SlideSpec; ind
|
|||||||
// ── DIAGRAM ──────────────────────────────────────────────────────────
|
// ── DIAGRAM ──────────────────────────────────────────────────────────
|
||||||
case 'diagram':
|
case 'diagram':
|
||||||
return (
|
return (
|
||||||
<div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', padding: '48px 60px', background: isDark ? palette.bg : '#111827', textAlign: 'left' }}>
|
<div style={{ width: '100%', height: '100%', display: 'flex', flexDirection: 'column', padding: '48px 60px', textAlign: 'left' }}>
|
||||||
<h2 style={{ margin: 0, fontSize: 30, fontWeight: 800, letterSpacing: '-0.04em', color: '#fff' }}>{slide.title}</h2>
|
<h2 style={{ margin: 0, fontSize: 30, fontWeight: 800, letterSpacing: '-0.04em', color: text }}>{slide.title}</h2>
|
||||||
{slide.subtitle && <p style={{ margin: '6px 0 0', fontSize: 15, color: 'rgba(255,255,255,0.5)' }}>{slide.subtitle}</p>}
|
{slide.subtitle && <p style={{ margin: '6px 0 0', fontSize: 15, color: muted }}>{slide.subtitle}</p>}
|
||||||
<div style={{ flex: 1, marginTop: 20, overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
<div style={accentBar} />
|
||||||
{slide.mermaid ? <MermaidDiagram chart={slide.mermaid} isDark={true} /> : <p style={{ color: 'rgba(255,255,255,0.3)' }}>No diagram</p>}
|
<div style={{ flex: 1, overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
||||||
|
{slide.mermaid ? <MermaidDiagram chart={slide.mermaid} isDark={isDark} /> : <p style={{ color: muted }}>No diagram</p>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -45,17 +45,16 @@ Available slide types:
|
|||||||
- "summary": title, items[] (conclusion with checkmarks)
|
- "summary": title, items[] (conclusion with checkmarks)
|
||||||
|
|
||||||
RULES:
|
RULES:
|
||||||
- MAXIMUM 8 slides total — NEVER exceed 8, even for long notes
|
- Count the plain-text words in the note (ignore markdown, URLs, metadata, headers)
|
||||||
- Count only plain-text words, ignore markdown syntax, URLs, and metadata
|
- <50 words → 2-3 slides MAXIMUM (title + 1 content + summary)
|
||||||
- For short notes (<100 plain words): 3-4 slides max
|
- 50-150 words → 3-4 slides max
|
||||||
- For medium notes (100-300 plain words): 5-6 slides max
|
- 150-400 words → 5-6 slides max
|
||||||
- For any other note: 7-8 slides max — HARD LIMIT
|
- >400 words → 7-8 slides max — HARD LIMIT, NEVER exceed 8
|
||||||
- First slide MUST be type "title"
|
- First slide MUST be type "title"
|
||||||
- Last slide MUST be type "summary"
|
- Last slide MUST be type "summary"
|
||||||
- Include at most 1 "chart" or "stats" slide — only if real numeric data exists in the note
|
- Include "chart" ONLY if real numeric data exists in the note — otherwise FORBIDDEN
|
||||||
- Use VARIED types — never 2 identical types in a row
|
- Use VARIED types — never 2 identical types in a row
|
||||||
- All text content must come from the source notes (never invent data)
|
- All text must come from the source note — never invent data`,
|
||||||
- Each bullet/card must be a real sentence (15+ words, not generic)`,
|
|
||||||
inputSchema: z.object({
|
inputSchema: z.object({
|
||||||
title: z.string().describe('Short presentation title (6 words max)'),
|
title: z.string().describe('Short presentation title (6 words max)'),
|
||||||
theme: z.string().optional().describe('Visual recipe: architectural-saas, midnight-cathedral, aurora-borealis, venture-pitch, clinical-precision, coastal-morning, etc.'),
|
theme: z.string().optional().describe('Visual recipe: architectural-saas, midnight-cathedral, aurora-borealis, venture-pitch, clinical-precision, coastal-morning, etc.'),
|
||||||
@@ -64,9 +63,11 @@ RULES:
|
|||||||
|
|
||||||
execute: async ({ title, theme, slides }) => {
|
execute: async ({ title, theme, slides }) => {
|
||||||
try {
|
try {
|
||||||
console.log('[Slides Tool] Building presentation:', title, '| Slides:', slides.length, '| Theme:', theme)
|
// Hard cap: never more than 8 slides regardless of what the model outputs
|
||||||
|
const cappedSlides = slides.slice(0, 8)
|
||||||
|
console.log('[Slides Tool] Building presentation:', title, '| Slides:', cappedSlides.length, '| Theme:', theme)
|
||||||
|
|
||||||
const html = buildPresentationHTML({ title, theme, slides: slides as any })
|
const html = buildPresentationHTML({ title, theme, slides: cappedSlides as any })
|
||||||
|
|
||||||
const canvas = await prisma.canvas.create({
|
const canvas = await prisma.canvas.create({
|
||||||
data: {
|
data: {
|
||||||
@@ -75,15 +76,15 @@ RULES:
|
|||||||
type: 'slides',
|
type: 'slides',
|
||||||
title: title || 'Présentation',
|
title: title || 'Présentation',
|
||||||
html,
|
html,
|
||||||
slideCount: slides.length,
|
slideCount: cappedSlides.length,
|
||||||
theme: theme || 'architectural-saas',
|
theme: theme || 'architectural-saas',
|
||||||
spec: { title, theme, slides },
|
spec: { title, theme, slides: cappedSlides },
|
||||||
}),
|
}),
|
||||||
userId: ctx.userId,
|
userId: ctx.userId,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log('[Slides Tool] Canvas created:', canvas.id, '| Slides:', slides.length, '| Size:', Math.round(html.length / 1024), 'KB')
|
console.log('[Slides Tool] Canvas created:', canvas.id, '| Slides:', cappedSlides.length, '| Size:', Math.round(html.length / 1024), 'KB')
|
||||||
|
|
||||||
// Decrement slide_generate quota after successful canvas creation
|
// Decrement slide_generate quota after successful canvas creation
|
||||||
incrementUsageAsync(ctx.userId, 'slide_generate')
|
incrementUsageAsync(ctx.userId, 'slide_generate')
|
||||||
@@ -94,7 +95,7 @@ RULES:
|
|||||||
data: {
|
data: {
|
||||||
status: 'success',
|
status: 'success',
|
||||||
result: canvas.id,
|
result: canvas.id,
|
||||||
log: `Slides generated: ${slides.length} slides, ${Math.round(html.length / 1024)}KB`,
|
log: `Slides generated: ${cappedSlides.length} slides, ${Math.round(html.length / 1024)}KB`,
|
||||||
},
|
},
|
||||||
}).catch(err => console.error('[Slides Tool] Failed to update action status:', err))
|
}).catch(err => console.error('[Slides Tool] Failed to update action status:', err))
|
||||||
}
|
}
|
||||||
@@ -103,8 +104,8 @@ RULES:
|
|||||||
success: true,
|
success: true,
|
||||||
canvasId: canvas.id,
|
canvasId: canvas.id,
|
||||||
canvasName: canvas.name,
|
canvasName: canvas.name,
|
||||||
slideCount: slides.length,
|
slideCount: cappedSlides.length,
|
||||||
message: `Presentation "${canvas.name}" created with ${slides.length} slides.`,
|
message: `Presentation "${canvas.name}" created with ${cappedSlides.length} slides.`,
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.error('[Slides Tool] FATAL:', e)
|
console.error('[Slides Tool] FATAL:', e)
|
||||||
|
|||||||
Reference in New Issue
Block a user