After extensive investigation, I've identified TWO CRITICAL MISMATCHES causing labels to appear on connection lines instead of box corners:
In label_renderer.go:
if e.diagram.Type == "sequence" && e.diagramScrollOffset > 7 {
// Sticky headers are activeIn tuieditor.go (actual rendering):
if e.diagram.Type == "sequence" && headerLines > 0 && startLine > headerLines {
// We're scrolled past the headers - make them stickyThe label renderer uses a hardcoded value of 7, while the actual rendering uses dynamically calculated headerLines (which can vary based on the actual header content).
When sticky headers ARE active, the label calculation is:
viewportY = pos.Y + 1 + scrollIndicatorLinesWith pos.Y = 2 and scrollIndicatorLines = 1, this gives viewportY = 4.
BUT, based on actual rendering output:
- Line 1: Scroll indicator
- Line 2: Empty space
- Line 3: Empty space
- Line 4: Box top border (╭──────╮) <- Where label SHOULD be
- Line 5: Participant text (│ Client │) <- Where label APPEARS
- Line 6: Box bottom border (╰──────╯)
The formula should be:
viewportY = pos.Y + 2 + scrollIndicatorLines- Participants are positioned at Y=2 in the diagram coordinate system
- The box top border renders at Y=2 in diagram coordinates
- When converted to viewport coordinates with sticky headers:
- Current calculation:
2 + 1 + 1 = 4(points to line 4 in 0-indexed, but ANSI uses 1-indexed) - Actual need:
2 + 2 + 1 = 5to account for the additional offset in sticky header rendering
- Current calculation:
The rendering pipeline has multiple coordinate systems:
- Diagram coordinates: Where nodes are positioned (Y=2 for participants)
- Canvas coordinates: After applying scroll offset
- Viewport coordinates: After adding UI elements (scroll indicators, headers)
- ANSI coordinates: 1-indexed for terminal display
The label renderer is missing the correct transformation between these systems when sticky headers are active.
In editor/label_renderer.go, line 49:
// CURRENT (WRONG):
viewportY = pos.Y + 1 + scrollIndicatorLines
// SHOULD BE:
viewportY = pos.Y + 2 + scrollIndicatorLinesAND check for sticky headers should match the actual rendering logic:
// Instead of hardcoded 7, use the actual header size calculation- Connection labels use separate logic from node labels, potentially causing inconsistencies
- Multiple coordinate transformation implementations exist across the codebase with subtle differences
- The header size calculation is duplicated in multiple places rather than being centralized
The test output clearly shows:
- Box top border at line 4 (1-indexed)
- Label calculation gives Y=4 (which in 1-indexed ANSI becomes line 4)
- But the actual box corner is at line 4, not line 5
This off-by-one error is consistent across all scrolled scenarios with sticky headers.