Files
next-ai-draw-io/tests/unit/fixtures/engine-vpc-multiaz.drawio

1 line
15 KiB
Plaintext
Raw Normal View History

feat(diagram-engine): style markers + XML→tree reverse parser Groundwork for a declarative diagram engine where the model declares nesting and the engine computes every coordinate, instead of the model emitting raw mxCell XML. The design keeps the canvas as the SINGLE source of truth: the node tree is never persisted, it is re-derived from the current canvas XML whenever needed. A user's manual edits are therefore an input to the next re-layout, not a second copy of the state that has to be reconciled. Two behaviours this relies on, both verified against the real embedded editor with a Playwright mouse drag (reading the editor's own autosave payload): 1. An AWS group stencil WITHOUT container=1 does not get a dragged shape reparented — parent stays "1" and geometry stays absolute. WITH container=1 it does: parent becomes the frame, geometry becomes parent-relative. So the engine must stamp container=1 on every container it emits. 2. draw.io preserves style keys it does not understand, and resolves a duplicate key last-wins. So dai_* markers survive a user edit, and container=1 can be appended to a catalog style without first parsing out an existing value — which matters because the AWS catalog is inconsistent about it (group_vpc, group_region, group_subnet ship without it; group_account ships with it). markers.ts — dai_kind / dai_dir / dai_gap / dai_cols / dai_pin, and the container token normalisation. types.ts — the node tree contract, plus a `foreign` bucket so cells the engine does not understand (user annotations, imported shapes) round-trip verbatim rather than being destroyed by a re-layout. parse.ts — XML → tree. Handles all four icon encodings (resIcon=, bare shape=, shape=image data URI, grIcon=), resolves nesting from parent with a geometry fallback for frames that lack container=1, recovers layout direction from the marker or infers it from child positions, and survives cycles, compressed files and multi-page decks. 75 unit tests, including a round-trip against real output from the reference project's build_vpc.mjs. One finding worth recording: the reference project's "phantom" node (a wrapper that participates in layout but emits no cell) makes the round-trip lossy by construction. In build_vpc.mjs a phantom erased a container's col direction — its children were reparented onto the grandparent, leaving a 2-D arrangement the parser can only read as a grid. 26 of the reference project's 31 examples use phantoms, so our engine needs an invisible-but-real container instead. Tracked separately.
2026-08-09 11:35:32 +09:00
<mxfile host="app.diagrams.net"><diagram name="VPC Multi-AZ 3-tier" id="d"><mxGraphModel dx="1400" dy="900" grid="0" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1183" pageHeight="1150" math="0" shadow="0"><root><mxCell id="0"/><mxCell id="1" parent="0"/><mxCell id="users" value="Users / Internet" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#DAE8FC;strokeColor=#6C8EBF;fontColor=#1A1A1A;fontSize=11;fontStyle=1;verticalAlign=middle;" vertex="1" parent="1"><mxGeometry x="50" y="555" width="130" height="80" as="geometry"/></mxCell><mxCell id="region" value="Region (ap-southeast-1)" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_region;strokeColor=#879196;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#879196;dashed=1;strokeColor=#147EBA;" vertex="1" parent="1"><mxGeometry x="250" y="100" width="883" height="990" as="geometry"/></mxCell><mxCell id="vpc" value="VPC 10.0.0.0/16" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_vpc;strokeColor=#879196;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#879196;dashed=0;strokeColor=#8C4FFF;" vertex="1" parent="region"><mxGeometry x="24" y="60" width="568" height="906" as="geometry"/></mxCell><mxCell id="igw" value="Internet Gateway" style="sketch=0;outlineConnect=0;fontColor=#232F3E;gradientColor=none;strokeColor=#232F3E;fillColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.internet_gateway;" vertex="1" parent="vpc"><mxGeometry x="260" y="60" width="48" height="48" as="geometry"/></mxCell><mxCell id="alb" value="Application Load Balancer (Multi-AZ)" style="sketch=0;outlineConnect=0;fontColor=#232F3E;gradientColor=none;fillColor=#8C4FFF;strokeColor=none;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;pointerEvents=1;shape=mxgraph.aws4.application_load_balancer;" vertex="1" parent="vpc"><mxGeometry x="260" y="164" width="48" height="48" as="geometry"/></mxCell><mxCell id="az_a" value="Availability Zone A" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_availability_zone;strokeColor=#545B64;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#545B64;dashed=1;strokeColor=#2F9491;" vertex="1" parent="vpc"><mxGeometry x="24" y="268" width="235" height="614" as="geometry"/></mxCell><mxCell id="pub_a" value="Public Subnet" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_subnet;strokeColor=#879196;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#879196;dashed=0;fillColor=light-dark(#eef5e6,#1a2410);strokeColor=#7AA116;" vertex="1" parent="az_a"><mxGeometry x="24" y="60" width="187" height="166" as="geometry"/></mxCell><mxCell id="nat_a" value="NAT Gateway" style="sketch=0;outlineConnect=0;fontColor=#232F3E;gradientColor=none;strokeColor=#232F3E;fillColor=#ffffff;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;fontSize=12;fontStyle=0;aspect=fixed;shape=mxgraph.aws4.resourceIcon;resIcon=mxgraph.aws4.nat_gateway;" vertex="1" parent="pub_a"><mxGeometry x="70" y="60" width="48" height="48" as="geometry"/></mxCell><mxCell id="app_a" value="Private Subnet (App)" style="sketch=0;outlineConnect=0;gradientColor=none;html=1;whiteSpace=wrap;fontSize=12;fontStyle=0;shape=mxgraph.aws4.group;grIcon=mxgraph.aws4.group_subnet;strokeColor=#879196;fillColor=none;verticalAlign=top;align=left;spacingLeft=30;fontColor=#879196;dashed=0;fillColor=light-dark(#e6f4f4,#0f2424);strokeColor=#00A4A6;" vertex="1" parent="az_a"><mxGeometry x="24" y="242" width