feat(diagram): named styles and compact cells for shorter model output (#969)

The model defines reused styles once as <mxStyle name="..." value="..."/> and
refers to them by name, and writes shapes as one self-closing mxCell with
x, y, w, h and edges with source and target. style-classes.ts and
compact-cells.ts expand both back into standard draw.io XML after validation
and add draw.io's html=1 / whiteSpace=wrap defaults; the diagram shown to the
model is folded into the same notation. Prompts, tool descriptions and the MCP
drawing guide teach the notation with shared examples.

Measured on the five start-screen examples against main: gpt-6-luna 35% fewer
output tokens, Claude Opus 5.5 41% fewer. Two five-model review rounds fixed
quote-aware cell matching, attribute escaping, edge/vertex inference, root-id
and layer handling, and several auto-fix gaps.
This commit is contained in:
Dayuan Jiang
2026-10-10 21:58:21 +09:00
committed by GitHub
parent 498ee628f1
commit cd5352ca88
16 changed files with 1720 additions and 123 deletions
+7 -2
View File
@@ -8,8 +8,10 @@ import type { LoadMode } from "@/contexts/diagram-context"
import type { ValidationResult } from "@/lib/diagram-validator"
import { formatValidationFeedback } from "@/lib/diagram-validator"
import { isMxCellXmlComplete } from "@/lib/utils"
import { foldCells } from "@/packages/mcp-server/src/compact-cells.ts"
import { editDiagram } from "@/packages/mcp-server/src/edit-diagram.ts"
import { prepareNewDiagram } from "@/packages/mcp-server/src/new-diagram.ts"
import { hasCells } from "@/packages/mcp-server/src/pages.ts"
const DEBUG = process.env.NODE_ENV === "development"
@@ -516,7 +518,7 @@ ${finalXml}
Current diagram XML:
\`\`\`xml
${currentXml}
${foldCells(currentXml)}
\`\`\`
Please check the cell IDs and retry.`,
@@ -545,7 +547,7 @@ Please check the cell IDs and retry.`,
Current diagram XML:
\`\`\`xml
${currentXml || "No XML available"}
${currentXml ? foldCells(currentXml) : "No XML available"}
\`\`\`
Please check cell IDs and retry, or use display_diagram to regenerate.`,
@@ -575,7 +577,10 @@ Use display_diagram to create the complete diagram, or edit_diagram to change th
// Detect if LLM incorrectly started fresh instead of continuing
// LLM should only output bare mxCells now, so wrapper tags indicate error
const trimmed = xml.trim()
// Definitions at the start are a restart only once cells were written
const isFreshStart =
(trimmed.startsWith("<mxStyle") &&
hasCells(partialXmlRef.current)) ||
trimmed.startsWith("<mxGraphModel") ||
trimmed.startsWith("<root") ||
trimmed.startsWith("<mxfile") ||