feat: add slides generation tool with multiple slide types
Some checks failed
CI / Lint, Test & Build (push) Failing after 17s
CI / Deploy production (on server) (push) Has been skipped

- 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>
This commit is contained in:
Antigravity
2026-05-22 17:18:48 +00:00
parent 0f6b9509da
commit 5728452b4a
68 changed files with 6990 additions and 2584 deletions

View File

@@ -1,10 +1,11 @@
/**
* Note CRUD Tools
* note_create, note_read, note_update
* note_create, note_read, note_update, note_find_and_update
*/
import { tool } from 'ai'
import { z } from 'zod'
import { Prisma } from '@prisma/client'
import { toolRegistry } from './registry'
import { prisma } from '@/lib/prisma'
import { markdownToHtml } from '@/lib/markdown-to-html'
@@ -104,3 +105,85 @@ toolRegistry.register({
},
}),
})
// --- note_find_and_update ---
toolRegistry.register({
name: 'note_find_and_update',
description: 'Find a note by searching its title/content, then append, prepend, or replace information in it. Use this when the user says "find the note about X and add Y to it".',
isInternal: true,
buildTool: (ctx) =>
tool({
description: 'Find a note by a search query, then update its content (append/prepend/replace).',
inputSchema: z.object({
query: z.string().describe('Search query to find the note (e.g. "bugs and new features")'),
newContent: z.string().describe('Content to add to the note (markdown supported)'),
operation: z.enum(['append', 'prepend', 'replace']).default('append').describe('append: add to end, prepend: add to start, replace: overwrite'),
}),
execute: async ({ query, newContent, operation }) => {
try {
// FTS search for best matching note
const results = await prisma.$queryRaw<Array<{ id: string; title: string | null; content: string | null }>>(
Prisma.sql`
SELECT id, title, content
FROM "Note"
WHERE "tsv" @@ plainto_tsquery('simple', ${query})
AND "trashedAt" IS NULL
AND "isArchived" = false
AND "userId" = ${ctx.userId}
ORDER BY ts_rank("tsv", plainto_tsquery('simple', ${query})) DESC
LIMIT 1`
)
if (!results || results.length === 0) {
// Fallback: simple title/content ILIKE search
const fallback = await prisma.note.findFirst({
where: {
userId: ctx.userId,
trashedAt: null,
isArchived: false,
OR: [
{ title: { contains: query, mode: 'insensitive' } },
{ content: { contains: query, mode: 'insensitive' } },
],
},
select: { id: true, title: true, content: true },
})
if (!fallback) {
return { error: `No note found matching "${query}". Try a different search term.` }
}
results.push(fallback)
}
const note = results[0]
let updatedContent: string
switch (operation) {
case 'append':
updatedContent = note.content ? `${note.content}\n\n${newContent}` : newContent
break
case 'prepend':
updatedContent = note.content ? `${newContent}\n\n${note.content}` : newContent
break
case 'replace':
default:
updatedContent = newContent
}
await prisma.note.update({
where: { id: note.id },
data: { content: updatedContent, updatedAt: new Date() },
})
return {
success: true,
noteId: note.id,
noteTitle: note.title || 'Untitled',
operation,
message: `Successfully updated note "${note.title || 'Untitled'}" (${operation}).`,
}
} catch (e: any) {
return { error: `find_and_update failed: ${e.message}` }
}
},
}),
})