- Add slides.tool.ts with support for title, bullets, chart, stats, table, cards, timeline, quote, comparison, equation, image, summary slide types - Chart types: bar, horizontal-bar, line, donut, radar - Integrate with agent executor and canvas system - Add multilingual support (en/fr) - Various UI improvements and bug fixes Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
108 lines
5.6 KiB
TypeScript
108 lines
5.6 KiB
TypeScript
'use server'
|
|
|
|
import { tool } from 'ai'
|
|
import { z } from 'zod'
|
|
import { toolRegistry } from './registry'
|
|
import { prisma } from '@/lib/prisma'
|
|
import { buildPresentationHTML } from './slides-html-builder'
|
|
|
|
const slideSchema = z.discriminatedUnion('type', [
|
|
z.object({ type: z.literal('title'), title: z.string(), subtitle: z.string().optional() }),
|
|
z.object({ type: z.literal('bullets'), title: z.string(), items: z.array(z.string()) }),
|
|
z.object({ type: z.literal('chart'), title: z.string(), chartType: z.enum(['bar', 'horizontal-bar', 'line', 'donut', 'radar']), data: z.array(z.object({ label: z.string(), value: z.number() })), subtitle: z.string().optional() }),
|
|
z.object({ type: z.literal('stats'), title: z.string(), stats: z.array(z.object({ value: z.string(), label: z.string() })) }),
|
|
z.object({ type: z.literal('table'), title: z.string(), headers: z.array(z.string()), rows: z.array(z.array(z.string())) }),
|
|
z.object({ type: z.literal('cards'), title: z.string(), cards: z.array(z.object({ title: z.string(), description: z.string() })) }),
|
|
z.object({ type: z.literal('timeline'), title: z.string(), events: z.array(z.object({ date: z.string(), title: z.string(), description: z.string().optional() })) }),
|
|
z.object({ type: z.literal('quote'), quote: z.string(), author: z.string().optional(), context: z.string().optional() }),
|
|
z.object({ type: z.literal('comparison'), title: z.string(), left: z.object({ title: z.string(), points: z.array(z.string()), score: z.string().optional() }), right: z.object({ title: z.string(), points: z.array(z.string()), score: z.string().optional() }) }),
|
|
z.object({ type: z.literal('equation'), title: z.string(), equations: z.array(z.object({ latex: z.string(), label: z.string().optional() })), explanation: z.string().optional() }),
|
|
z.object({ type: z.literal('image'), title: z.string(), url: z.string().optional(), caption: z.string().optional() }),
|
|
z.object({ type: z.literal('summary'), title: z.string(), items: z.array(z.string()) }),
|
|
])
|
|
|
|
toolRegistry.register({
|
|
name: 'generate_slides',
|
|
description: 'Renders a structured presentation from JSON data into a full animated HTML file and saves it.',
|
|
isInternal: true,
|
|
buildTool: (ctx) =>
|
|
tool({
|
|
description: `Create a presentation from structured slide data. Each slide has a type and corresponding content.
|
|
|
|
Available slide types:
|
|
- "title": title, subtitle (opening slide with particles)
|
|
- "bullets": title, items[] (bullet list with 4-6 items of 15+ words each)
|
|
- "chart": title, chartType (bar|horizontal-bar|line|donut|radar), data[{label,value}], subtitle
|
|
- "stats": title, stats[{value:"98%", label:"KPI name"}] (3-4 animated KPIs)
|
|
- "table": title, headers[], rows[][] (data table)
|
|
- "cards": title, cards[{title, description}] (3-6 info cards)
|
|
- "timeline": title, events[{date, title, description}] (chronological events)
|
|
- "quote": quote, author, context (citation with analysis)
|
|
- "comparison": title, left{title, points[], score}, right{...} (A vs B)
|
|
- "equation": title, equations[{latex, label}], explanation (math formulas)
|
|
- "image": title, url, caption (image slide)
|
|
- "summary": title, items[] (conclusion with checkmarks)
|
|
|
|
RULES:
|
|
- 6-12 slides per presentation
|
|
- First slide MUST be type "title"
|
|
- Last slide MUST be type "summary"
|
|
- Include at least 1 "chart" or "stats" slide
|
|
- Use VARIED types — never 2 identical types in a row
|
|
- All text content must come from the source notes (never invent data)
|
|
- Each bullet/card must be a real sentence (15+ words, not generic)`,
|
|
inputSchema: z.object({
|
|
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.'),
|
|
slides: z.array(slideSchema).describe('Array of slide objects, each with a "type" field'),
|
|
}),
|
|
|
|
execute: async ({ title, theme, slides }) => {
|
|
try {
|
|
console.log('[Slides Tool] Building presentation:', title, '| Slides:', slides.length, '| Theme:', theme)
|
|
|
|
const html = buildPresentationHTML({ title, theme, slides: slides as any })
|
|
|
|
const canvas = await prisma.canvas.create({
|
|
data: {
|
|
name: title || 'Présentation',
|
|
data: JSON.stringify({
|
|
type: 'slides',
|
|
title: title || 'Présentation',
|
|
html,
|
|
slideCount: slides.length,
|
|
theme: theme || 'architectural-saas',
|
|
spec: { title, theme, slides },
|
|
}),
|
|
userId: ctx.userId,
|
|
},
|
|
})
|
|
|
|
console.log('[Slides Tool] Canvas created:', canvas.id, '| Slides:', slides.length, '| Size:', Math.round(html.length / 1024), 'KB')
|
|
|
|
if (ctx.actionId) {
|
|
await prisma.agentAction.update({
|
|
where: { id: ctx.actionId },
|
|
data: {
|
|
status: 'success',
|
|
result: canvas.id,
|
|
log: `Slides generated: ${slides.length} slides, ${Math.round(html.length / 1024)}KB`,
|
|
},
|
|
}).catch(err => console.error('[Slides Tool] Failed to update action status:', err))
|
|
}
|
|
|
|
return {
|
|
success: true,
|
|
canvasId: canvas.id,
|
|
canvasName: canvas.name,
|
|
slideCount: slides.length,
|
|
message: `Presentation "${canvas.name}" created with ${slides.length} slides.`,
|
|
}
|
|
} catch (e: any) {
|
|
console.error('[Slides Tool] FATAL:', e)
|
|
return { success: false, error: `Failed: ${e.message}` }
|
|
}
|
|
},
|
|
}),
|
|
})
|