From 72e5320044c6fc600506eada9e6e0307faec6c19 Mon Sep 17 00:00:00 2001 From: Eugene Mikhantyev Date: Thu, 12 Mar 2026 23:57:39 +0000 Subject: [PATCH] feat: add get_schematic_view tool for rasterized schematic images Exports schematic to SVG via kicad-cli, then converts to PNG using cairosvg (same approach as get_board_2d_view). Falls back gracefully to SVG if cairosvg is not installed. Supports configurable output size and format (png/svg). Co-Authored-By: Claude Opus 4.6 --- python/kicad_interface.py | 72 +++++++++++++++++++++++++++++++++++++++ src/tools/registry.ts | 1 + src/tools/schematic.ts | 55 ++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) diff --git a/python/kicad_interface.py b/python/kicad_interface.py index 2d23559..826f67d 100644 --- a/python/kicad_interface.py +++ b/python/kicad_interface.py @@ -383,6 +383,7 @@ class KiCADInterface: "generate_netlist": self._handle_generate_netlist, "sync_schematic_to_board": self._handle_sync_schematic_to_board, "list_schematic_libraries": self._handle_list_schematic_libraries, + "get_schematic_view": self._handle_get_schematic_view, "list_schematic_components": self._handle_list_schematic_components, "list_schematic_nets": self._handle_list_schematic_nets, "list_schematic_wires": self._handle_list_schematic_wires, @@ -1420,6 +1421,77 @@ class KiCADInterface: logger.error(traceback.format_exc()) return {"success": False, "message": str(e)} + def _handle_get_schematic_view(self, params): + """Get a rasterised image of the schematic (SVG export → optional PNG conversion)""" + logger.info("Getting schematic view") + import subprocess + import tempfile + import base64 + + try: + schematic_path = params.get("schematicPath") + if not schematic_path or not os.path.exists(schematic_path): + return {"success": False, "message": f"Schematic not found: {schematic_path}"} + + fmt = params.get("format", "png") + width = params.get("width", 1200) + height = params.get("height", 900) + + # Step 1: Export schematic to SVG via kicad-cli + with tempfile.TemporaryDirectory() as tmpdir: + svg_path = os.path.join(tmpdir, "schematic.svg") + cmd = ["kicad-cli", "sch", "export", "svg", + "--output", tmpdir, "--no-background-color", + schematic_path] + result = subprocess.run(cmd, capture_output=True, text=True, timeout=60) + + if result.returncode != 0: + return {"success": False, "message": f"kicad-cli SVG export failed: {result.stderr}"} + + # kicad-cli may name the file after the schematic, find it + import glob + svg_files = glob.glob(os.path.join(tmpdir, "*.svg")) + if not svg_files: + return {"success": False, "message": "No SVG file produced by kicad-cli"} + svg_path = svg_files[0] + + if fmt == "svg": + with open(svg_path, "r", encoding="utf-8") as f: + svg_data = f.read() + return {"success": True, "imageData": svg_data, "format": "svg"} + + # Step 2: Convert SVG to PNG using cairosvg + try: + from cairosvg import svg2png + except ImportError: + # Fallback: return SVG data with a note + with open(svg_path, "r", encoding="utf-8") as f: + svg_data = f.read() + return { + "success": True, + "imageData": svg_data, + "format": "svg", + "message": "cairosvg not installed — returning SVG instead of PNG. Install with: pip install cairosvg", + } + + png_data = svg2png(url=svg_path, output_width=width, output_height=height) + + return { + "success": True, + "imageData": base64.b64encode(png_data).decode("utf-8"), + "format": "png", + "width": width, + "height": height, + } + + except FileNotFoundError: + return {"success": False, "message": "kicad-cli not found in PATH"} + except Exception as e: + logger.error(f"Error getting schematic view: {e}") + import traceback + logger.error(traceback.format_exc()) + return {"success": False, "message": str(e)} + def _handle_list_schematic_components(self, params): """List all components in a schematic""" logger.info("Listing schematic components") diff --git a/src/tools/registry.ts b/src/tools/registry.ts index ea9db8b..9841461 100644 --- a/src/tools/registry.ts +++ b/src/tools/registry.ts @@ -103,6 +103,7 @@ export const toolCategories: ToolCategory[] = [ "list_schematic_labels", "generate_netlist", "sync_schematic_to_board", + "get_schematic_view", "export_schematic_svg", "export_schematic_pdf" ] diff --git a/src/tools/schematic.ts b/src/tools/schematic.ts index cdf86bb..a083099 100644 --- a/src/tools/schematic.ts +++ b/src/tools/schematic.ts @@ -888,6 +888,61 @@ Note: operates on .kicad_sch files only. To modify a PCB footprint use edit_comp }, ); + // Get schematic view (rasterized image) + server.tool( + "get_schematic_view", + "Return a rasterized image of the schematic (PNG by default, or SVG). Uses kicad-cli to export SVG, then converts to PNG via cairosvg. Use this for visual feedback after placing or wiring components.", + { + schematicPath: z.string().describe("Path to the .kicad_sch file"), + format: z + .enum(["png", "svg"]) + .optional() + .describe("Output format (default: png)"), + width: z.number().optional().describe("Image width in pixels (default: 1200)"), + height: z.number().optional().describe("Image height in pixels (default: 900)"), + }, + async (args: { + schematicPath: string; + format?: "png" | "svg"; + width?: number; + height?: number; + }) => { + const result = await callKicadScript("get_schematic_view", args); + if (result.success) { + if (result.format === "svg") { + return { + content: [ + { + type: "text", + text: result.message + ? `SVG data returned (${result.message})` + : `SVG schematic view (${result.imageData?.length || 0} chars)`, + }, + ], + }; + } + // PNG — return as base64 image + return { + content: [ + { + type: "image" as const, + data: result.imageData, + mimeType: "image/png", + }, + ], + }; + } + return { + content: [ + { + type: "text", + text: `Failed to get schematic view: ${result.message || "Unknown error"}`, + }, + ], + }; + }, + ); + // Run Electrical Rules Check (ERC) server.tool( "run_erc",