Files
Momento/memento-note/lib/ai/tools/chart.tool.ts
Antigravity beca2c52c3
Some checks failed
CI / Lint, Test & Build (push) Failing after 15s
CI / Deploy production (on server) (push) Has been skipped
feat: add inline chart support for notes
- Add NoteChart component using Recharts (bar, line, area, pie, radar, funnel, gauge)
- Add generate_chart and insert_chart_in_note AI tools
- Add chart code block support in MarkdownContent (```chart ... ```)
- Support JSON and simple data formats (label: value)
- Add i18n translations for chart features

Chart syntax examples:
- JSON: ```chart {"type":"bar","data":[{"label":"A","value":10}]} ```
- Simple: ```chart\nbar\nSales Data\nJan: 120\nFeb: 150\n```

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 17:29:10 +00:00

118 lines
5.2 KiB
TypeScript

/**
* Chart Tool for Notes
* Allows AI to generate inline charts from note data
*/
import { tool } from 'ai'
import { z } from 'zod'
import { toolRegistry } from './registry'
// Chart generation tool - inserts chart as markdown code block
toolRegistry.register({
name: 'generate_chart',
description: 'Generate an inline chart from data and insert it into a note as markdown. The chart will be rendered directly in the note.',
isInternal: true,
buildTool: (ctx) =>
tool({
description: `Generate an inline chart from data and return it as a markdown code block that can be inserted into a note.
Available chart types:
- "bar": Vertical bar chart
- "horizontal-bar": Horizontal bar chart
- "line": Line chart for trends over time
- "area": Area chart (filled line)
- "pie": Pie/donut chart for proportions
- "radar": Radar chart for comparing multiple dimensions
- "funnel": Funnel chart for stages/conversions
- "gauge": Gauge/meter for a single value
Data format: Array of objects with "label" (string) and "value" (number).
IMPORTANT:
- Extract data from the note content when possible
- Keep labels short (max 20 characters)
- Round values to reasonable precision (max 2 decimal places)
- For time series, use short date formats (Jan, Feb, Mar or 2023, 2024)
- Maximum 12 data points for readability
- The output will be rendered as a visual chart in the note`,
inputSchema: z.object({
chartType: z.enum(['bar', 'horizontal-bar', 'line', 'area', 'pie', 'radar', 'funnel', 'gauge']).describe('Type of chart to generate'),
title: z.string().optional().describe('Optional title for the chart'),
data: z.array(z.object({
label: z.string().describe('Label for the data point (e.g., month name, category)'),
value: z.number().describe('Numeric value for the data point'),
})).describe('Array of data points with label and value'),
insertLocation: z.enum(['append', 'prepend', 'replace']).default('append').describe('Where to insert the chart in the note'),
targetNoteId: z.string().optional().describe('Optional: specific note ID to update. If not provided, use the current note.'),
}),
execute: async ({ chartType, title, data, insertLocation, targetNoteId }) => {
try {
// Generate the markdown code block for the chart
const chartData = data.map(d => `${d.label}: ${d.value}`).join('\n')
const chartMarkdown = `\`\`\`chart
${chartType}${title ? `\n${title}` : ''}
${chartData}
\`\`\`\n`
return {
success: true,
chartMarkdown,
chartType,
dataPointCount: data.length,
message: `Chart generated with ${data.length} data points. Insert this markdown into the note.`,
}
} catch (e: any) {
return { success: false, error: `Chart generation failed: ${e.message}` }
}
},
}),
})
// Quick chart insert tool - directly updates a note with a chart
toolRegistry.register({
name: 'insert_chart_in_note',
description: 'Insert a chart directly into a note. Reads the note, extracts relevant data, generates a chart, and updates the note content.',
isInternal: true,
buildTool: (ctx) =>
tool({
description: `Insert a chart directly into a note. This tool will:
1. Read the current note content
2. Extract relevant data from the note (sales, metrics, comparisons, etc.)
3. Generate an appropriate chart type based on the data
4. Insert the chart markdown into the note
Use this when the user says "make a chart", "create a graph", "visualize this data", etc.
Choose the chart type based on the data:
- Use "bar" for comparing values across categories (default)
- Use "horizontal-bar" when labels are long
- Use "line" or "area" for time series or trends
- Use "pie" for showing proportions/percentages
- Use "radar" for comparing multiple attributes
- Use "funnel" for stages or conversion data
- Use "gauge" for progress/KPI (single value)`,
inputSchema: z.object({
noteId: z.string().describe('The note ID to read and update'),
chartHint: z.string().optional().describe('Optional hint about what to chart (e.g., "sales by month", "comparison of products")'),
insertLocation: z.enum(['append', 'prepend', 'before-section', 'after-section']).default('append').describe('Where to insert the chart'),
sectionMarker: z.string().optional().describe('For before-section/after-section: the heading text to find (e.g., "## Sales Data")'),
}),
execute: async ({ noteId, chartHint, insertLocation, sectionMarker }) => {
try {
// We'll return the instructions for the AI to format the chart
// The actual note update will be done by note_find_and_update or note_update
return {
success: true,
instructions: 'Generate a chart markdown block using the generate_chart tool, then use note_update or note_find_and_update to insert it into the note.',
noteId,
chartHint,
insertLocation,
sectionMarker,
}
} catch (e: any) {
return { success: false, error: `Failed to prepare chart insertion: ${e.message}` }
}
},
}),
})