diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md
index 489a520f2..4d0ab566d 100644
--- a/ci/release/changelogs/next.md
+++ b/ci/release/changelogs/next.md
@@ -1,5 +1,7 @@
#### Features ๐
+- `animated` keyword implemented for connections. [#652](https://github.com/terrastruct/d2/pull/652)
+
#### Improvements ๐งน
- ELK layouts tuned to have better defaults. [#627](https://github.com/terrastruct/d2/pull/627)
diff --git a/d2renderers/d2sketch/sketch.go b/d2renderers/d2sketch/sketch.go
index 4c4913a2b..13beb9b97 100644
--- a/d2renderers/d2sketch/sketch.go
+++ b/d2renderers/d2sketch/sketch.go
@@ -63,26 +63,6 @@ func DefineFillPattern() string {
`, fillPattern)
}
-func shapeStyle(shape d2target.Shape) string {
- out := ""
-
- if shape.Type == d2target.ShapeSQLTable || shape.Type == d2target.ShapeClass {
- out += fmt.Sprintf(`fill:%s;`, shape.Stroke)
- out += fmt.Sprintf(`stroke:%s;`, shape.Fill)
- } else {
- out += fmt.Sprintf(`fill:%s;`, shape.Fill)
- out += fmt.Sprintf(`stroke:%s;`, shape.Stroke)
- }
- out += fmt.Sprintf(`opacity:%f;`, shape.Opacity)
- out += fmt.Sprintf(`stroke-width:%d;`, shape.StrokeWidth)
- if shape.StrokeDash != 0 {
- dashSize, gapSize := svg.GetStrokeDashAttributes(float64(shape.StrokeWidth), shape.StrokeDash)
- out += fmt.Sprintf(`stroke-dasharray:%f,%f;`, dashSize, gapSize)
- }
-
- return out
-}
-
func Rect(r *Runner, shape d2target.Shape) (string, error) {
js := fmt.Sprintf(`node = rc.rectangle(0, 0, %d, %d, {
fill: "%s",
@@ -98,7 +78,7 @@ func Rect(r *Runner, shape d2target.Shape) (string, error) {
for _, p := range paths {
output += fmt.Sprintf(
``,
- shape.Pos.X, shape.Pos.Y, p, shapeStyle(shape),
+ shape.Pos.X, shape.Pos.Y, p, shape.CSSStyle(),
)
}
output += fmt.Sprintf(
@@ -123,7 +103,7 @@ func Oval(r *Runner, shape d2target.Shape) (string, error) {
for _, p := range paths {
output += fmt.Sprintf(
``,
- shape.Pos.X, shape.Pos.Y, p, shapeStyle(shape),
+ shape.Pos.X, shape.Pos.Y, p, shape.CSSStyle(),
)
}
output += fmt.Sprintf(
@@ -150,7 +130,7 @@ func Paths(r *Runner, shape d2target.Shape, paths []string) (string, error) {
for _, p := range sketchPaths {
output += fmt.Sprintf(
``,
- p, shapeStyle(shape),
+ p, shape.CSSStyle(),
)
}
for _, p := range sketchPaths {
@@ -163,20 +143,6 @@ func Paths(r *Runner, shape d2target.Shape, paths []string) (string, error) {
return output, nil
}
-func connectionStyle(connection d2target.Connection) string {
- out := ""
-
- out += fmt.Sprintf(`stroke:%s;`, connection.Stroke)
- out += fmt.Sprintf(`opacity:%f;`, connection.Opacity)
- out += fmt.Sprintf(`stroke-width:%d;`, connection.StrokeWidth)
- if connection.StrokeDash != 0 {
- dashSize, gapSize := svg.GetStrokeDashAttributes(float64(connection.StrokeWidth), connection.StrokeDash)
- out += fmt.Sprintf(`stroke-dasharray:%f,%f;`, dashSize, gapSize)
- }
-
- return out
-}
-
func Connection(r *Runner, connection d2target.Connection, path, attrs string) (string, error) {
roughness := 1.0
js := fmt.Sprintf(`node = rc.path("%s", {roughness: %f, seed: 1});`, path, roughness)
@@ -185,10 +151,14 @@ func Connection(r *Runner, connection d2target.Connection, path, attrs string) (
return "", err
}
output := ""
+ animatedClass := ""
+ if connection.Animated {
+ animatedClass = " animated-connection"
+ }
for _, p := range paths {
output += fmt.Sprintf(
- ``,
- p, connectionStyle(connection), attrs,
+ ``,
+ animatedClass, p, connection.CSSStyle(), attrs,
)
}
return output, nil
@@ -210,7 +180,7 @@ func Table(r *Runner, shape d2target.Shape) (string, error) {
for _, p := range paths {
output += fmt.Sprintf(
``,
- shape.Pos.X, shape.Pos.Y, p, shapeStyle(shape),
+ shape.Pos.X, shape.Pos.Y, p, shape.CSSStyle(),
)
}
@@ -338,7 +308,7 @@ func Class(r *Runner, shape d2target.Shape) (string, error) {
for _, p := range paths {
output += fmt.Sprintf(
``,
- shape.Pos.X, shape.Pos.Y, p, shapeStyle(shape),
+ shape.Pos.X, shape.Pos.Y, p, shape.CSSStyle(),
)
}
diff --git a/d2renderers/d2sketch/sketch_test.go b/d2renderers/d2sketch/sketch_test.go
index c2b8dadea..f35eb1be7 100644
--- a/d2renderers/d2sketch/sketch_test.go
+++ b/d2renderers/d2sketch/sketch_test.go
@@ -39,6 +39,11 @@ func TestSketch(t *testing.T) {
script: `winter.snow -> summer.sun
`,
},
+ {
+ name: "animated",
+ script: `winter.snow -> summer.sun -> trees -> winter.snow: { style.animated: true }
+ `,
+ },
{
name: "connection label",
script: `a -> b: hello
diff --git a/d2renderers/d2sketch/testdata/animated/sketch.exp.svg b/d2renderers/d2sketch/testdata/animated/sketch.exp.svg
new file mode 100644
index 000000000..25a6c8c99
--- /dev/null
+++ b/d2renderers/d2sketch/testdata/animated/sketch.exp.svg
@@ -0,0 +1,77 @@
+
+
\ No newline at end of file
diff --git a/d2renderers/d2svg/class.go b/d2renderers/d2svg/class.go
index 85205db32..200264054 100644
--- a/d2renderers/d2svg/class.go
+++ b/d2renderers/d2svg/class.go
@@ -80,7 +80,7 @@ func classRow(shape d2target.Shape, box *geo.Box, prefix, nameText, typeText str
func drawClass(writer io.Writer, targetShape d2target.Shape) {
fmt.Fprintf(writer, ``,
- targetShape.Pos.X, targetShape.Pos.Y, targetShape.Width, targetShape.Height, shapeStyle(targetShape))
+ targetShape.Pos.X, targetShape.Pos.Y, targetShape.Width, targetShape.Height, targetShape.CSSStyle())
box := geo.NewBox(
geo.NewPoint(float64(targetShape.Pos.X), float64(targetShape.Pos.Y)),
diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go
index 2ff96d2eb..87bfff9a2 100644
--- a/d2renderers/d2svg/d2svg.go
+++ b/d2renderers/d2svg/d2svg.go
@@ -461,8 +461,12 @@ func drawConnection(writer io.Writer, labelMaskID string, connection d2target.Co
}
fmt.Fprintf(writer, out)
} else {
- fmt.Fprintf(writer, ``,
- path, connectionStyle(connection), attrs)
+ animatedClass := ""
+ if connection.Animated {
+ animatedClass = " animated-connection"
+ }
+ fmt.Fprintf(writer, ``,
+ path, animatedClass, connection.CSSStyle(), attrs)
}
if connection.Label != "" {
@@ -582,7 +586,7 @@ func render3dRect(targetShape d2target.Shape) string {
)
border := targetShape
border.Fill = "none"
- borderStyle := shapeStyle(border)
+ borderStyle := border.CSSStyle()
renderedBorder := fmt.Sprintf(``,
strings.Join(borderSegments, " "), borderStyle)
@@ -603,7 +607,7 @@ func render3dRect(targetShape d2target.Shape) string {
mainShape := targetShape
mainShape.Stroke = "none"
mainRect := fmt.Sprintf(``,
- targetShape.Pos.X, targetShape.Pos.Y, targetShape.Width, targetShape.Height, shapeStyle(mainShape), maskID,
+ targetShape.Pos.X, targetShape.Pos.Y, targetShape.Width, targetShape.Height, mainShape.CSSStyle(), maskID,
)
// render the side shapes in the darkened color without stroke and the border mask
@@ -628,7 +632,7 @@ func render3dRect(targetShape d2target.Shape) string {
sideShape.Fill = darkerColor
sideShape.Stroke = "none"
renderedSides := fmt.Sprintf(``,
- strings.Join(sidePoints, " "), shapeStyle(sideShape), maskID)
+ strings.Join(sidePoints, " "), sideShape.CSSStyle(), maskID)
return borderMask + mainRect + renderedSides + renderedBorder
}
@@ -643,7 +647,7 @@ func drawShape(writer io.Writer, targetShape d2target.Shape, sketchRunner *d2ske
tl := geo.NewPoint(float64(targetShape.Pos.X), float64(targetShape.Pos.Y))
width := float64(targetShape.Width)
height := float64(targetShape.Height)
- style := shapeStyle(targetShape)
+ style := targetShape.CSSStyle()
shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[targetShape.Type]
s := shape.NewShape(shapeType, geo.NewBox(tl, width, height))
@@ -939,43 +943,6 @@ func RenderText(text string, x, height float64) string {
return strings.Join(rendered, "")
}
-func shapeStyle(shape d2target.Shape) string {
- out := ""
-
- if shape.Type == d2target.ShapeSQLTable || shape.Type == d2target.ShapeClass {
- // Fill is used for header fill in these types
- // This fill property is just background of rows
- out += fmt.Sprintf(`fill:%s;`, shape.Stroke)
- // Stroke (border) of these shapes should match the header fill
- out += fmt.Sprintf(`stroke:%s;`, shape.Fill)
- } else {
- out += fmt.Sprintf(`fill:%s;`, shape.Fill)
- out += fmt.Sprintf(`stroke:%s;`, shape.Stroke)
- }
- out += fmt.Sprintf(`opacity:%f;`, shape.Opacity)
- out += fmt.Sprintf(`stroke-width:%d;`, shape.StrokeWidth)
- if shape.StrokeDash != 0 {
- dashSize, gapSize := svg.GetStrokeDashAttributes(float64(shape.StrokeWidth), shape.StrokeDash)
- out += fmt.Sprintf(`stroke-dasharray:%f,%f;`, dashSize, gapSize)
- }
-
- return out
-}
-
-func connectionStyle(connection d2target.Connection) string {
- out := ""
-
- out += fmt.Sprintf(`stroke:%s;`, connection.Stroke)
- out += fmt.Sprintf(`opacity:%f;`, connection.Opacity)
- out += fmt.Sprintf(`stroke-width:%d;`, connection.StrokeWidth)
- if connection.StrokeDash != 0 {
- dashSize, gapSize := svg.GetStrokeDashAttributes(float64(connection.StrokeWidth), connection.StrokeDash)
- out += fmt.Sprintf(`stroke-dasharray:%f,%f;`, dashSize, gapSize)
- }
-
- return out
-}
-
func embedFonts(buf *bytes.Buffer, fontFamily *d2fonts.FontFamily) {
content := buf.String()
buf.WriteString(`your love life will behappyharmoniousboredomimmortalityFridayMondayInsomniaSleepWakeDreamListenTalk hear
+
+
+
\ No newline at end of file
diff --git a/e2etests/testdata/stable/animated/elk/board.exp.json b/e2etests/testdata/stable/animated/elk/board.exp.json
new file mode 100644
index 000000000..92badcbf0
--- /dev/null
+++ b/e2etests/testdata/stable/animated/elk/board.exp.json
@@ -0,0 +1,864 @@
+{
+ "name": "",
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "your love life will be",
+ "type": "",
+ "pos": {
+ "x": 111,
+ "y": 12
+ },
+ "width": 247,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F7F8FE",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "your love life will be",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 147,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "happy",
+ "type": "",
+ "pos": {
+ "x": 12,
+ "y": 238
+ },
+ "width": 149,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F7F8FE",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "happy",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 49,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "harmonious",
+ "type": "",
+ "pos": {
+ "x": 181,
+ "y": 238
+ },
+ "width": 190,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F7F8FE",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "harmonious",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 90,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "boredom",
+ "type": "",
+ "pos": {
+ "x": 402,
+ "y": 12
+ },
+ "width": 168,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F7F8FE",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "boredom",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 68,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "immortality",
+ "type": "",
+ "pos": {
+ "x": 391,
+ "y": 238
+ },
+ "width": 191,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F7F8FE",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "immortality",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 91,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "Friday",
+ "type": "",
+ "pos": {
+ "x": 607,
+ "y": 12
+ },
+ "width": 150,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F7F8FE",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "Friday",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 50,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "Monday",
+ "type": "",
+ "pos": {
+ "x": 602,
+ "y": 238
+ },
+ "width": 161,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F7F8FE",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "Monday",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 61,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "Insomnia",
+ "type": "",
+ "pos": {
+ "x": 935,
+ "y": 12
+ },
+ "width": 170,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F7F8FE",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "Insomnia",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 70,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "Sleep",
+ "type": "",
+ "pos": {
+ "x": 783,
+ "y": 238
+ },
+ "width": 145,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F7F8FE",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "Sleep",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 45,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "Wake",
+ "type": "",
+ "pos": {
+ "x": 948,
+ "y": 238
+ },
+ "width": 144,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F7F8FE",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "Wake",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 44,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "Dream",
+ "type": "",
+ "pos": {
+ "x": 1112,
+ "y": 238
+ },
+ "width": 151,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F7F8FE",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "Dream",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 51,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "Listen",
+ "type": "",
+ "pos": {
+ "x": 1225,
+ "y": 12
+ },
+ "width": 148,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F7F8FE",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "Listen",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 48,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "Talk",
+ "type": "",
+ "pos": {
+ "x": 1231,
+ "y": 464
+ },
+ "width": 136,
+ "height": 126,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F7F8FE",
+ "stroke": "#0D32B2",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "Talk",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#0A0F25",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 36,
+ "labelHeight": 26,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ }
+ ],
+ "connections": [
+ {
+ "id": "(your love life will be -> happy)[0]",
+ "src": "your love life will be",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "happy",
+ "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": 193.66666666666652,
+ "y": 138
+ },
+ {
+ "x": 193.66666666666652,
+ "y": 188
+ },
+ {
+ "x": 86.5,
+ "y": 188
+ },
+ {
+ "x": 86.5,
+ "y": 238
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(your love life will be -> harmonious)[0]",
+ "src": "your love life will be",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "harmonious",
+ "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": 275.9999999999999,
+ "y": 138
+ },
+ {
+ "x": 276,
+ "y": 238
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(boredom <- immortality)[0]",
+ "src": "boredom",
+ "srcArrow": "triangle",
+ "srcLabel": "",
+ "dst": "immortality",
+ "dstArrow": "none",
+ "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": 486.5,
+ "y": 138
+ },
+ {
+ "x": 486.5,
+ "y": 238
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(Friday <-> Monday)[0]",
+ "src": "Friday",
+ "srcArrow": "triangle",
+ "srcLabel": "",
+ "dst": "Monday",
+ "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": 682.5,
+ "y": 138
+ },
+ {
+ "x": 682.5,
+ "y": 238
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(Insomnia -- Sleep)[0]",
+ "src": "Insomnia",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "Sleep",
+ "dstArrow": "none",
+ "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": 977.5,
+ "y": 138
+ },
+ {
+ "x": 977.5,
+ "y": 188
+ },
+ {
+ "x": 855.5,
+ "y": 188
+ },
+ {
+ "x": 855.5,
+ "y": 238
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(Insomnia -- Wake)[0]",
+ "src": "Insomnia",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "Wake",
+ "dstArrow": "none",
+ "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": 1020,
+ "y": 138
+ },
+ {
+ "x": 1020,
+ "y": 238
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(Insomnia -- Dream)[0]",
+ "src": "Insomnia",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "Dream",
+ "dstArrow": "none",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 8,
+ "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": 1062.5,
+ "y": 138
+ },
+ {
+ "x": 1062.5,
+ "y": 188
+ },
+ {
+ "x": 1187.5,
+ "y": 188
+ },
+ {
+ "x": 1187.5,
+ "y": 238
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(Listen <-> Talk)[0]",
+ "src": "Listen",
+ "srcArrow": "cf-one",
+ "srcLabel": "",
+ "dst": "Talk",
+ "dstArrow": "diamond",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "#0D32B2",
+ "label": "hear",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "#676C7E",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 32,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 1299,
+ "y": 138
+ },
+ {
+ "x": 1299,
+ "y": 464
+ }
+ ],
+ "animated": true,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ]
+}
diff --git a/e2etests/testdata/stable/animated/elk/sketch.exp.svg b/e2etests/testdata/stable/animated/elk/sketch.exp.svg
new file mode 100644
index 000000000..5d3c01de3
--- /dev/null
+++ b/e2etests/testdata/stable/animated/elk/sketch.exp.svg
@@ -0,0 +1,65 @@
+
+
\ No newline at end of file