Export diagrams as interactive HTML from the app and an agent tool

This commit is contained in:
Ian Gulliver
2026-10-23 08:12:23 -07:00
parent caf5b123d2
commit f18dd88465
7 changed files with 269 additions and 19 deletions
+45 -2
View File
@@ -44,7 +44,50 @@ func (m *Manager) renderDiagramTool() mcp.Tool {
}
}
func renderDiagram(ctx context.Context, source string) ([]byte, error) {
func (m *Manager) exportDiagramTool(l *live) mcp.Tool {
return mcp.Tool{
Name: "export_diagram",
Description: "Export a diagram as a self-contained HTML page, drawn exactly as byre's app lays it out and as interactive as its full-size view: clicking a box highlights it, its arrows and its neighbors, each highlight line is a button, and it zooms and pans. " +
"Pass the body of a ```diagram block and the file to write; show it with show_page.",
InputSchema: json.RawMessage(`{
"type": "object",
"properties": {
"source": {"type": "string", "description": "The block's body, without the fence lines."},
"path": {"type": "string", "description": "The HTML file to write, relative to your worktree, absolute, or starting with ~/."}
},
"required": ["source", "path"]
}`),
Handler: func(ctx context.Context, raw json.RawMessage) (string, error) {
args := struct {
Source string `json:"source"`
Path string `json:"path"`
}{}
err := json.Unmarshal(raw, &args)
if err != nil {
return "", fmt.Errorf("malformed export_diagram call: %w", err)
}
full, err := l.resolve(args.Path)
if err != nil {
return "", err
}
html, err := renderDiagram(ctx, args.Source, "--html")
if err != nil {
return "", err
}
err = os.MkdirAll(filepath.Dir(full), 0o755)
if err != nil {
return "", err
}
err = os.WriteFile(full, html, 0o644)
if err != nil {
return "", err
}
return fmt.Sprintf("Wrote the diagram to %s.", args.Path), nil
},
}
}
func renderDiagram(ctx context.Context, source string, flags ...string) ([]byte, error) {
exe, err := os.Executable()
if err != nil {
return nil, err
@@ -52,7 +95,7 @@ func renderDiagram(ctx context.Context, source string) ([]byte, error) {
app := filepath.Join(filepath.Dir(exe), "..", "MacOS", "Byre")
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, app, "render-diagram")
cmd := exec.CommandContext(ctx, app, append([]string{"render-diagram"}, flags...)...)
cmd.Stdin = strings.NewReader(source)
stderr := bytes.Buffer{}
cmd.Stderr = &stderr