diff --git a/d2layouts/d2sequence/sequence_diagram.go b/d2layouts/d2sequence/sequence_diagram.go
index c0f3727ee..bf7622a33 100644
--- a/d2layouts/d2sequence/sequence_diagram.go
+++ b/d2layouts/d2sequence/sequence_diagram.go
@@ -20,6 +20,7 @@ type sequenceDiagram struct {
lifelines []*d2graph.Edge
actors []*d2graph.Object
spans []*d2graph.Object
+ notes []*d2graph.Object
// can be either actors or spans
// rank: left to right position of actors/spans (spans have the same rank as their parents)
@@ -30,23 +31,59 @@ type sequenceDiagram struct {
minMessageRank map[*d2graph.Object]int
maxMessageRank map[*d2graph.Object]int
- messageYStep float64
+ yStep float64
actorXStep float64
maxActorHeight float64
+
+ verticalIndices map[string]int
+}
+
+func getObjEarliestLineNum(o *d2graph.Object) int {
+ min := int(math.MaxInt64)
+ for _, ref := range o.References {
+ if ref.MapKey == nil {
+ continue
+ }
+ min = go2.IntMin(min, ref.MapKey.Range.Start.Line)
+ }
+ return min
+}
+
+func getEdgeEarliestLineNum(e *d2graph.Edge) int {
+ min := int(math.MaxInt64)
+ for _, ref := range e.References {
+ if ref.MapKey == nil {
+ continue
+ }
+ min = go2.IntMin(min, ref.MapKey.Range.Start.Line)
+ }
+ return min
+}
+
+func hasEdge(o *d2graph.Object) bool {
+ for _, ref := range o.References {
+ if ref.MapKey != nil && len(ref.MapKey.Edges) > 0 {
+ return true
+ }
+ }
+
+ return false
}
func newSequenceDiagram(actors []*d2graph.Object, messages []*d2graph.Edge) *sequenceDiagram {
sd := &sequenceDiagram{
- messages: messages,
- actors: actors,
- spans: nil,
- lifelines: nil,
- objectRank: make(map[*d2graph.Object]int),
- minMessageRank: make(map[*d2graph.Object]int),
- maxMessageRank: make(map[*d2graph.Object]int),
- messageYStep: MIN_MESSAGE_DISTANCE,
- actorXStep: MIN_ACTOR_DISTANCE,
- maxActorHeight: 0.,
+ messages: messages,
+ actors: actors,
+ spans: nil,
+ notes: nil,
+ lifelines: nil,
+ objectRank: make(map[*d2graph.Object]int),
+ minMessageRank: make(map[*d2graph.Object]int),
+ maxMessageRank: make(map[*d2graph.Object]int),
+ yStep: MIN_MESSAGE_DISTANCE,
+ actorXStep: MIN_ACTOR_DISTANCE,
+ maxActorHeight: 0.,
+ verticalIndices: make(map[string]int),
}
for rank, actor := range actors {
@@ -63,21 +100,34 @@ func newSequenceDiagram(actors []*d2graph.Object, messages []*d2graph.Edge) *seq
queue := make([]*d2graph.Object, len(actor.ChildrenArray))
copy(queue, actor.ChildrenArray)
for len(queue) > 0 {
- span := queue[0]
+ child := queue[0]
queue = queue[1:]
- // spans are always rectangles and have no labels
- span.Attributes.Label = d2graph.Scalar{Value: ""}
- span.Attributes.Shape = d2graph.Scalar{Value: shape.SQUARE_TYPE}
- sd.spans = append(sd.spans, span)
- sd.objectRank[span] = rank
+ // spans are children of actors that have edges
+ // notes are children of actors with no edges
+ if hasEdge(child) {
+ // spans have no labels
+ // TODO why not? Spans should be able to
+ child.Attributes.Label = d2graph.Scalar{Value: ""}
+ child.Attributes.Shape = d2graph.Scalar{Value: shape.SQUARE_TYPE}
+ sd.spans = append(sd.spans, child)
+ sd.objectRank[child] = rank
+ } else {
+ sd.verticalIndices[child.AbsID()] = getObjEarliestLineNum(child)
+ // TODO change to page type when it doesn't look deformed
+ child.Attributes.Shape = d2graph.Scalar{Value: shape.SQUARE_TYPE}
+ sd.notes = append(sd.notes, child)
+ sd.objectRank[child] = rank
+ child.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
+ }
- queue = append(queue, span.ChildrenArray...)
+ queue = append(queue, child.ChildrenArray...)
}
}
for rank, message := range sd.messages {
- sd.messageYStep = math.Max(sd.messageYStep, float64(message.LabelDimensions.Height))
+ sd.verticalIndices[message.AbsID()] = getEdgeEarliestLineNum(message)
+ sd.yStep = math.Max(sd.yStep, float64(message.LabelDimensions.Height))
sd.setMinMaxMessageRank(message.Src, rank)
sd.setMinMaxMessageRank(message.Dst, rank)
@@ -89,7 +139,7 @@ func newSequenceDiagram(actors []*d2graph.Object, messages []*d2graph.Edge) *seq
sd.actorXStep = math.Max(sd.actorXStep, distributedLabelWidth+HORIZONTAL_PAD)
}
- sd.messageYStep += VERTICAL_PAD
+ sd.yStep += VERTICAL_PAD
sd.maxActorHeight += VERTICAL_PAD
if sd.root.LabelHeight != nil {
sd.maxActorHeight += float64(*sd.root.LabelHeight)
@@ -111,6 +161,7 @@ func (sd *sequenceDiagram) setMinMaxMessageRank(actor *d2graph.Object, rank int)
func (sd *sequenceDiagram) layout() {
sd.placeActors()
sd.placeSpans()
+ sd.placeNotes()
sd.routeMessages()
sd.addLifelineEdges()
}
@@ -142,7 +193,17 @@ func (sd *sequenceDiagram) placeActors() {
// │
// │
func (sd *sequenceDiagram) addLifelineEdges() {
- endY := sd.getMessageY(len(sd.messages))
+ endY := 0.
+ for _, m := range sd.messages {
+ for _, p := range m.Route {
+ endY = math.Max(endY, p.Y)
+ }
+ }
+ for _, note := range sd.notes {
+ endY = math.Max(endY, note.TopLeft.Y+note.Height)
+ }
+ endY += sd.yStep
+
for _, actor := range sd.actors {
actorBottom := actor.Center()
actorBottom.Y = actor.TopLeft.Y + actor.Height
@@ -169,6 +230,31 @@ func (sd *sequenceDiagram) addLifelineEdges() {
}
}
+func (sd *sequenceDiagram) placeNotes() {
+ rankToX := make(map[int]float64)
+ for _, actor := range sd.actors {
+ rankToX[sd.objectRank[actor]] = actor.Center().X
+ }
+
+ for i, note := range sd.notes {
+ verticalIndex := sd.verticalIndices[note.AbsID()]
+ y := sd.maxActorHeight + sd.yStep
+
+ for _, msg := range sd.messages {
+ if sd.verticalIndices[msg.AbsID()] < verticalIndex {
+ y += sd.yStep
+ }
+ }
+ for _, otherNote := range sd.notes[:i] {
+ y += otherNote.Height + sd.yStep
+ }
+
+ x := rankToX[sd.objectRank[note]] - (note.Width / 2.)
+ note.Box.TopLeft = geo.NewPoint(x, y)
+ note.ZIndex = 1
+ }
+}
+
// placeSpans places spans over the object lifeline
// ┌──────────┐
// │ actor │
@@ -261,6 +347,13 @@ func (sd *sequenceDiagram) routeMessages() {
}
messageY := sd.getMessageY(rank)
+
+ for _, note := range sd.notes {
+ if sd.verticalIndices[note.AbsID()] < sd.verticalIndices[message.AbsID()] {
+ messageY += note.Height + sd.yStep
+ }
+ }
+
message.Route = []*geo.Point{
geo.NewPoint(startX, messageY),
geo.NewPoint(endX, messageY),
@@ -274,7 +367,7 @@ func (sd *sequenceDiagram) routeMessages() {
func (sd *sequenceDiagram) getMessageY(rank int) float64 {
// +1 so that the first message has the top padding for its label
- return ((float64(rank) + 1.) * sd.messageYStep) + sd.maxActorHeight
+ return ((float64(rank) + 1.) * sd.yStep) + sd.maxActorHeight
}
func (sd *sequenceDiagram) isActor(obj *d2graph.Object) bool {
diff --git a/e2etests/stable_test.go b/e2etests/stable_test.go
index 306838bd4..6492bfe9c 100644
--- a/e2etests/stable_test.go
+++ b/e2etests/stable_test.go
@@ -1332,6 +1332,19 @@ z -> z: hello
label: hello
icon: https://icons.terrastruct.com/essentials/time.svg
}
+`,
+ },
+ {
+ name: "sequence_diagram_note",
+ script: `shape: sequence_diagram
+a; b; c; d
+a -> b
+a.explanation
+a.another explanation
+b -> c
+b."Some one who believes imaginary things\n appear right before your i's."
+c -> b: okay
+d."The earth is like a tiny grain of sand, only much, much heavier"
`,
},
}
diff --git a/e2etests/testdata/stable/sequence_diagram_note/dagre/board.exp.json b/e2etests/testdata/stable/sequence_diagram_note/dagre/board.exp.json
new file mode 100644
index 000000000..426562e61
--- /dev/null
+++ b/e2etests/testdata/stable/sequence_diagram_note/dagre/board.exp.json
@@ -0,0 +1,592 @@
+{
+ "name": "",
+ "shapes": [
+ {
+ "id": "a",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 50
+ },
+ "width": 150,
+ "height": 169,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#E3E9FD",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 12,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "b",
+ "type": "",
+ "pos": {
+ "x": 400,
+ "y": 52
+ },
+ "width": 150,
+ "height": 167,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#E3E9FD",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 13,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "c",
+ "type": "",
+ "pos": {
+ "x": 800,
+ "y": 52
+ },
+ "width": 150,
+ "height": 167,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F7F8FE",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 13,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "d",
+ "type": "",
+ "pos": {
+ "x": 1200,
+ "y": 52
+ },
+ "width": 150,
+ "height": 167,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#E3E9FD",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 13,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "a.explanation",
+ "type": "rectangle",
+ "pos": {
+ "x": -20,
+ "y": 479
+ },
+ "width": 190,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#EDF0FD",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "explanation",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 90,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 1,
+ "level": 2
+ },
+ {
+ "id": "a.another explanation",
+ "type": "rectangle",
+ "pos": {
+ "x": -50,
+ "y": 735
+ },
+ "width": 250,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#EDF0FD",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "another explanation",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 150,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 1,
+ "level": 2
+ },
+ {
+ "id": "b.\"Some one who believes imaginary things\\n appear right before your i's.\"",
+ "type": "rectangle",
+ "pos": {
+ "x": 278,
+ "y": 1121
+ },
+ "width": 393,
+ "height": 142,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#EDF0FD",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "Some one who believes imaginary things\n appear right before your i's.",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 293,
+ "labelHeight": 42,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 1,
+ "level": 2
+ },
+ {
+ "id": "d.The earth is like a tiny grain of sand, only much, much heavier",
+ "type": "rectangle",
+ "pos": {
+ "x": 1004,
+ "y": 1523
+ },
+ "width": 541,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#EDF0FD",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "The earth is like a tiny grain of sand, only much, much heavier",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 441,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 1,
+ "level": 2
+ }
+ ],
+ "connections": [
+ {
+ "id": "(a -> b)[0]",
+ "src": "a",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "b",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "#0D32B2",
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 75,
+ "y": 349
+ },
+ {
+ "x": 475,
+ "y": 349
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 2
+ },
+ {
+ "id": "(b -> c)[0]",
+ "src": "b",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "c",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "#0D32B2",
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 475,
+ "y": 991
+ },
+ {
+ "x": 875,
+ "y": 991
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 2
+ },
+ {
+ "id": "(c -> b)[0]",
+ "src": "c",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "b",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "#0D32B2",
+ "label": "okay",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 33,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 875,
+ "y": 1393
+ },
+ {
+ "x": 475,
+ "y": 1393
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 2
+ },
+ {
+ "id": "(a -- )[0]",
+ "src": "a",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "a-lifeline-end-2251863791",
+ "dstArrow": "none",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 8,
+ "strokeWidth": 2,
+ "stroke": "#0D32B2",
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 75,
+ "y": 219
+ },
+ {
+ "x": 75,
+ "y": 1779
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(b -- )[0]",
+ "src": "b",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "b-lifeline-end-668380428",
+ "dstArrow": "none",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 8,
+ "strokeWidth": 2,
+ "stroke": "#0D32B2",
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 475,
+ "y": 219
+ },
+ {
+ "x": 475,
+ "y": 1779
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(c -- )[0]",
+ "src": "c",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "c-lifeline-end-955173837",
+ "dstArrow": "none",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 8,
+ "strokeWidth": 2,
+ "stroke": "#0D32B2",
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 875,
+ "y": 219
+ },
+ {
+ "x": 875,
+ "y": 1779
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(d -- )[0]",
+ "src": "d",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "d-lifeline-end-2106864010",
+ "dstArrow": "none",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 8,
+ "strokeWidth": 2,
+ "stroke": "#0D32B2",
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 1275,
+ "y": 219
+ },
+ {
+ "x": 1275,
+ "y": 1779
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ]
+}
diff --git a/e2etests/testdata/stable/sequence_diagram_note/dagre/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_note/dagre/sketch.exp.svg
new file mode 100644
index 000000000..c407a5874
--- /dev/null
+++ b/e2etests/testdata/stable/sequence_diagram_note/dagre/sketch.exp.svg
@@ -0,0 +1,34 @@
+
+
\ No newline at end of file
diff --git a/e2etests/testdata/stable/sequence_diagram_note/elk/board.exp.json b/e2etests/testdata/stable/sequence_diagram_note/elk/board.exp.json
new file mode 100644
index 000000000..426562e61
--- /dev/null
+++ b/e2etests/testdata/stable/sequence_diagram_note/elk/board.exp.json
@@ -0,0 +1,592 @@
+{
+ "name": "",
+ "shapes": [
+ {
+ "id": "a",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 50
+ },
+ "width": 150,
+ "height": 169,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#E3E9FD",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "a",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 12,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "b",
+ "type": "",
+ "pos": {
+ "x": 400,
+ "y": 52
+ },
+ "width": 150,
+ "height": 167,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#E3E9FD",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "b",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 13,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "c",
+ "type": "",
+ "pos": {
+ "x": 800,
+ "y": 52
+ },
+ "width": 150,
+ "height": 167,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F7F8FE",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "c",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 13,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "d",
+ "type": "",
+ "pos": {
+ "x": 1200,
+ "y": 52
+ },
+ "width": 150,
+ "height": 167,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#E3E9FD",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "d",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 13,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "a.explanation",
+ "type": "rectangle",
+ "pos": {
+ "x": -20,
+ "y": 479
+ },
+ "width": 190,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#EDF0FD",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "explanation",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 90,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 1,
+ "level": 2
+ },
+ {
+ "id": "a.another explanation",
+ "type": "rectangle",
+ "pos": {
+ "x": -50,
+ "y": 735
+ },
+ "width": 250,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#EDF0FD",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "another explanation",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 150,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 1,
+ "level": 2
+ },
+ {
+ "id": "b.\"Some one who believes imaginary things\\n appear right before your i's.\"",
+ "type": "rectangle",
+ "pos": {
+ "x": 278,
+ "y": 1121
+ },
+ "width": 393,
+ "height": 142,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#EDF0FD",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "Some one who believes imaginary things\n appear right before your i's.",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 293,
+ "labelHeight": 42,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 1,
+ "level": 2
+ },
+ {
+ "id": "d.The earth is like a tiny grain of sand, only much, much heavier",
+ "type": "rectangle",
+ "pos": {
+ "x": 1004,
+ "y": 1523
+ },
+ "width": 541,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#EDF0FD",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "The earth is like a tiny grain of sand, only much, much heavier",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 441,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 1,
+ "level": 2
+ }
+ ],
+ "connections": [
+ {
+ "id": "(a -> b)[0]",
+ "src": "a",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "b",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "#0D32B2",
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 75,
+ "y": 349
+ },
+ {
+ "x": 475,
+ "y": 349
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 2
+ },
+ {
+ "id": "(b -> c)[0]",
+ "src": "b",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "c",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "#0D32B2",
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 475,
+ "y": 991
+ },
+ {
+ "x": 875,
+ "y": 991
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 2
+ },
+ {
+ "id": "(c -> b)[0]",
+ "src": "c",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "b",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "#0D32B2",
+ "label": "okay",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 33,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 875,
+ "y": 1393
+ },
+ {
+ "x": 475,
+ "y": 1393
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 2
+ },
+ {
+ "id": "(a -- )[0]",
+ "src": "a",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "a-lifeline-end-2251863791",
+ "dstArrow": "none",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 8,
+ "strokeWidth": 2,
+ "stroke": "#0D32B2",
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 75,
+ "y": 219
+ },
+ {
+ "x": 75,
+ "y": 1779
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(b -- )[0]",
+ "src": "b",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "b-lifeline-end-668380428",
+ "dstArrow": "none",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 8,
+ "strokeWidth": 2,
+ "stroke": "#0D32B2",
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 475,
+ "y": 219
+ },
+ {
+ "x": 475,
+ "y": 1779
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(c -- )[0]",
+ "src": "c",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "c-lifeline-end-955173837",
+ "dstArrow": "none",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 8,
+ "strokeWidth": 2,
+ "stroke": "#0D32B2",
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 875,
+ "y": 219
+ },
+ {
+ "x": 875,
+ "y": 1779
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(d -- )[0]",
+ "src": "d",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "d-lifeline-end-2106864010",
+ "dstArrow": "none",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 8,
+ "strokeWidth": 2,
+ "stroke": "#0D32B2",
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 1275,
+ "y": 219
+ },
+ {
+ "x": 1275,
+ "y": 1779
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ]
+}
diff --git a/e2etests/testdata/stable/sequence_diagram_note/elk/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_note/elk/sketch.exp.svg
new file mode 100644
index 000000000..c407a5874
--- /dev/null
+++ b/e2etests/testdata/stable/sequence_diagram_note/elk/sketch.exp.svg
@@ -0,0 +1,34 @@
+
+
\ No newline at end of file