mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-10-11 03:59:58 +08:00
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:
@@ -10,8 +10,18 @@ import {
|
||||
extractCompleteMxCells,
|
||||
replaceNodes,
|
||||
} from "@/lib/utils"
|
||||
import {
|
||||
defaultLayerOf,
|
||||
expandCompactCells,
|
||||
} from "@/packages/mcp-server/src/compact-cells.ts"
|
||||
import { applyDiagramOperations } from "@/packages/mcp-server/src/diagram-operations.ts"
|
||||
import { BLANK_MXFILE } from "@/packages/mcp-server/src/pages.ts"
|
||||
import {
|
||||
addDefaultStyles,
|
||||
applyStyleClasses,
|
||||
edgeIdsOf,
|
||||
readStyleClasses,
|
||||
} from "@/packages/mcp-server/src/style-classes.ts"
|
||||
|
||||
// Helper to extract complete operations from streaming input
|
||||
function getCompleteOperations(
|
||||
@@ -71,9 +81,17 @@ export function useDiagramStreaming({
|
||||
// so far. The tool handler validates and loads the final diagram.
|
||||
const handleDisplayChart = useCallback(
|
||||
(xml: string) => {
|
||||
const completeCells = extractCompleteMxCells(xml || "")
|
||||
// Named styles come before the cells, so the ones written so
|
||||
// far are known by the time their cells stream in
|
||||
const { classes, xml: cellsXml } = readStyleClasses(xml || "")
|
||||
const completeCells = extractCompleteMxCells(cellsXml)
|
||||
if (!completeCells) return
|
||||
const convertedXml = convertToLegalXml(completeCells)
|
||||
const convertedXml = addDefaultStyles(
|
||||
applyStyleClasses(
|
||||
expandCompactCells(convertToLegalXml(completeCells)),
|
||||
classes,
|
||||
),
|
||||
)
|
||||
if (convertedXml === previousXML.current) return
|
||||
|
||||
// Skip this update while the cells written so far don't parse
|
||||
@@ -202,9 +220,21 @@ export function useDiagramStreaming({
|
||||
if (lastProcessedXmlRef.current.get(opsKey) === ops) continue
|
||||
lastProcessedXmlRef.current.set(opsKey, ops)
|
||||
try {
|
||||
const edges = edgeIdsOf(originalXml)
|
||||
const layer = defaultLayerOf(originalXml)
|
||||
const { result } = applyDiagramOperations(
|
||||
originalXml,
|
||||
completeOps,
|
||||
completeOps.map((op) =>
|
||||
op.new_xml
|
||||
? {
|
||||
...op,
|
||||
new_xml: addDefaultStyles(
|
||||
expandCompactCells(op.new_xml, layer),
|
||||
edges,
|
||||
),
|
||||
}
|
||||
: op,
|
||||
),
|
||||
)
|
||||
if (lastProcessedXmlRef.current.get(resultKey) === result) {
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user