diff --git a/__tests__/integration.spec.ts b/__tests__/integration.spec.ts index 0d889f6..bb05cfa 100644 --- a/__tests__/integration.spec.ts +++ b/__tests__/integration.spec.ts @@ -291,7 +291,7 @@ describe("integration tests", () => { it("should have all tools properly exported", () => { // Verify that the total number of tools is correct - expect(tools).toHaveLength(17); + expect(tools).toHaveLength(18); // Define the expected list of tool names const expectedToolNames = [ @@ -312,6 +312,7 @@ describe("integration tests", () => { "generate_graph_chart", "generate_parallel_chart", "generate_tree_chart", + "generate_area_chart", ]; // Verify that the actual exported tool names match expectations diff --git a/__tests__/snapshots/area-basic.png b/__tests__/snapshots/area-basic.png new file mode 100644 index 0000000..5529a47 Binary files /dev/null and b/__tests__/snapshots/area-basic.png differ diff --git a/__tests__/snapshots/area-custom.png b/__tests__/snapshots/area-custom.png new file mode 100644 index 0000000..68efb1e Binary files /dev/null and b/__tests__/snapshots/area-custom.png differ diff --git a/__tests__/snapshots/area-smooth.png b/__tests__/snapshots/area-smooth.png new file mode 100644 index 0000000..93a7352 Binary files /dev/null and b/__tests__/snapshots/area-smooth.png differ diff --git a/__tests__/snapshots/area-stacked.png b/__tests__/snapshots/area-stacked.png new file mode 100644 index 0000000..a9f2d12 Binary files /dev/null and b/__tests__/snapshots/area-stacked.png differ diff --git a/__tests__/tools/area.spec.ts b/__tests__/tools/area.spec.ts new file mode 100644 index 0000000..3e8fcef --- /dev/null +++ b/__tests__/tools/area.spec.ts @@ -0,0 +1,82 @@ +import { describe, expect, it } from "vitest"; +import { generateAreaChartTool } from "../../src/tools/area"; +import "../utils/matcher"; + +describe("Area Chart Tool", () => { + it("should generate a basic area chart", async () => { + const result = await generateAreaChartTool.run({ + data: [ + { time: "2015", value: 23 }, + { time: "2016", value: 32 }, + { time: "2017", value: 28 }, + { time: "2018", value: 45 }, + { time: "2019", value: 38 }, + { time: "2020", value: 52 }, + ], + title: "Basic Area Chart", + width: 600, + height: 400, + }); + + await expect(result).toImageEqual("area-basic"); + }); + + it("should generate a stacked area chart", async () => { + const result = await generateAreaChartTool.run({ + data: [ + { group: "Series A", time: "2015", value: 23 }, + { group: "Series A", time: "2016", value: 32 }, + { group: "Series A", time: "2017", value: 28 }, + { group: "Series B", time: "2015", value: 18 }, + { group: "Series B", time: "2016", value: 25 }, + { group: "Series B", time: "2017", value: 22 }, + ], + title: "Stacked Area Chart", + stack: true, + width: 600, + height: 400, + }); + + await expect(result).toImageEqual("area-stacked"); + }); + + it("should generate an area chart with custom styling", async () => { + const result = await generateAreaChartTool.run({ + data: [ + { time: "Q1", value: 100 }, + { time: "Q2", value: 150 }, + { time: "Q3", value: 120 }, + { time: "Q4", value: 180 }, + ], + title: "Custom Styled Area Chart", + width: 800, + height: 500, + theme: "dark", + style: { + backgroundColor: "#1a1a1a", + palette: ["#ff6b6b", "#4ecdc4", "#45b7d1"], + }, + }); + + await expect(result).toImageEqual("area-custom"); + }); + + it("should generate area chart with smooth curves", async () => { + const result = await generateAreaChartTool.run({ + data: [ + { time: "Jan", value: 10 }, + { time: "Feb", value: 25 }, + { time: "Mar", value: 15 }, + { time: "Apr", value: 35 }, + { time: "May", value: 20 }, + { time: "Jun", value: 40 }, + ], + title: "Smooth Area Chart", + smooth: true, + width: 600, + height: 400, + }); + + await expect(result).toImageEqual("area-smooth"); + }); +}); diff --git a/__tests__/tools/index.spec.ts b/__tests__/tools/index.spec.ts index 332e598..a162648 100644 --- a/__tests__/tools/index.spec.ts +++ b/__tests__/tools/index.spec.ts @@ -11,9 +11,9 @@ describe("tools index", () => { * Tool count validation * Validates that the total number of exported tools meets expectations (17 chart tools) */ - it("should export all 17 chart tools", () => { + it("should export all 18 chart tools", () => { // Validate that the tools array contains 17 chart tools - expect(tools).toHaveLength(17); + expect(tools).toHaveLength(18); }); /** @@ -74,6 +74,7 @@ describe("tools index", () => { "generate_graph_chart", // Graph chart "generate_parallel_chart", // Parallel coordinates chart "generate_tree_chart", // Tree chart + "generate_area_chart", // Area chart ]; // Get actual tool names and sort them diff --git a/package.json b/package.json index 1f159ee..8f38f52 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "mcp-echarts", "description": "❤️ Generate visual charts using Apache ECharts with AI MCP dynamically.", - "version": "0.6.1", + "version": "0.7.0", "main": "build/index.js", "scripts": { "test": "vitest", diff --git a/src/tools/area.ts b/src/tools/area.ts new file mode 100644 index 0000000..e238e28 --- /dev/null +++ b/src/tools/area.ts @@ -0,0 +1,48 @@ +import { z } from "zod"; +import { generateLineChartTool } from "./line"; + +// Extends generateLineChartTool implementation since area charts are line charts with area fill enabled +export const generateAreaChartTool = { + ...generateLineChartTool, + name: "generate_area_chart", + description: + "Generate an area chart to show data trends under continuous independent variables and observe the overall data trend, such as, displacement = velocity (average or instantaneous) × time: s = v × t. If the x-axis is time (t) and the y-axis is velocity (v) at each moment, an area chart allows you to observe the trend of velocity over time and infer the distance traveled by the area's size.", + inputSchema: generateLineChartTool.inputSchema.extend({ + data: z + .array( + z.object({ + group: z + .string() + .optional() + .describe( + "Group name for multiple series, required when stack is enabled", + ), + time: z.string(), + value: z.number(), + }), + ) + .describe( + "Data for area chart, such as, [{ time: '2015', value: 23 }, { time: '2016', value: 32 }]. For multiple series: [{ group: 'Series A', time: '2015', value: 23 }, { group: 'Series B', time: '2015', value: 18 }].", + ) + .nonempty({ message: "Area chart data cannot be empty." }), + }), + run: (params: { + axisXTitle?: string; + axisYTitle?: string; + data: Array<{ time: string; value: number; group?: string }>; + height: number; + showSymbol?: boolean; + smooth?: boolean; + stack?: boolean; + theme?: "default" | "dark"; + title?: string; + width: number; + outputType?: "png" | "svg" | "option"; + }) => { + // Force showArea to true for area chart + return generateLineChartTool.run({ + ...params, + showArea: true, + }); + }, +}; diff --git a/src/tools/index.ts b/src/tools/index.ts index fdc4120..82879f1 100644 --- a/src/tools/index.ts +++ b/src/tools/index.ts @@ -1,3 +1,4 @@ +import { generateAreaChartTool } from "./area"; import { generateBarChartTool } from "./bar"; import { generateBoxplotChartTool } from "./boxplot"; import { generateCandlestickChartTool } from "./candlestick"; @@ -18,6 +19,7 @@ import { generateTreemapChartTool } from "./treemap"; export const tools = [ generateEChartsTool, + generateAreaChartTool, generateLineChartTool, generateBarChartTool, generatePieChartTool, @@ -39,6 +41,7 @@ export const tools = [ // Re-export individual tools for convenient use in tests and other places export { generateEChartsTool, + generateAreaChartTool, generateLineChartTool, generateBarChartTool, generatePieChartTool,