@@ -177,6 +177,14 @@ export function convertV3HandleToV4(v3Handle: string): string {
177177 RightBottom : "bottom-right" ,
178178 LeftTop : "top-left" ,
179179 LeftBottom : "bottom-left" ,
180+
181+ // Legacy iOS dialect for the top/bottom-edge quarter handles (web v3 uses
182+ // RightTop/LeftBottom for the same ports). Without these they hit the
183+ // lowercase fallback below, producing invalid handle ids that mis-route.
184+ Topright : "top-right" ,
185+ Topleft : "top-left" ,
186+ Bottomright : "bottom-right" ,
187+ Bottomleft : "bottom-left" ,
180188 }
181189
182190 return handleMap [ v3Handle ] || v3Handle . toLowerCase ( )
@@ -367,6 +375,17 @@ function calculateRelativePosition(
367375 }
368376}
369377
378+ /**
379+ * Order child elements top-to-bottom by bounds.y. V3 stored class/object
380+ * attributes and methods as separate child elements and rendered them by
381+ * vertical position (UMLElement.verticallySortedChildren); object key order is
382+ * not a reliable proxy, so sort before folding them into a parent node.
383+ * Mutates in place — callers pass a fresh `Object.values(...)` array.
384+ */
385+ function sortedByBoundsY ( elements : V3UMLElement [ ] ) : V3UMLElement [ ] {
386+ return elements . sort ( ( a , b ) => ( a . bounds ?. y ?? 0 ) - ( b . bounds ?. y ?? 0 ) )
387+ }
388+
370389/**
371390 * Convert V3 node data to V4 node data format
372391 */
@@ -390,7 +409,7 @@ function convertV3NodeDataToV4(
390409 case "Enumeration" : {
391410 const attributes : Array < { id : string ; name : string } > = [ ]
392411 const methods : Array < { id : string ; name : string } > = [ ]
393- Object . values ( allElements ) . forEach ( ( childElement ) => {
412+ sortedByBoundsY ( Object . values ( allElements ) ) . forEach ( ( childElement ) => {
394413 if ( childElement . owner === element . id ) {
395414 if ( childElement . type === "ClassAttribute" ) {
396415 attributes . push ( {
@@ -441,7 +460,7 @@ function convertV3NodeDataToV4(
441460 const attributes : Array < { id : string ; name : string } > = [ ]
442461 const methods : Array < { id : string ; name : string } > = [ ]
443462
444- Object . values ( allElements ) . forEach ( ( childElement ) => {
463+ sortedByBoundsY ( Object . values ( allElements ) ) . forEach ( ( childElement ) => {
445464 if ( childElement . owner === element . id ) {
446465 if ( childElement . type === "ObjectAttribute" ) {
447466 attributes . push ( {
@@ -480,7 +499,7 @@ function convertV3NodeDataToV4(
480499 case "CommunicationObject" : {
481500 const attributes : Array < { id : string ; name : string } > = [ ]
482501 const methods : Array < { id : string ; name : string } > = [ ]
483- Object . values ( allElements ) . forEach ( ( childElement ) => {
502+ sortedByBoundsY ( Object . values ( allElements ) ) . forEach ( ( childElement ) => {
484503 if ( childElement . owner === element . id ) {
485504 if ( childElement . type === "ObjectAttribute" ) {
486505 attributes . push ( {
0 commit comments