diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md
index a50801ce1..af8e00b13 100644
--- a/ci/release/changelogs/next.md
+++ b/ci/release/changelogs/next.md
@@ -1,9 +1,9 @@
#### Features ๐
-- New class of special themes, starting with `Terminal` [#1040](https://github.com/terrastruct/d2/pull/1040)
+- New class of special themes, starting with `Terminal`, and `Terminal Grayscale` [#1040](https://github.com/terrastruct/d2/pull/1040), [#1041](https://github.com/terrastruct/d2/pull/1041)
- `style.font: mono` to use a monospaced font for the text/label [#1010](https://github.com/terrastruct/d2/pull/1010)
- `border-radius` is supported for both `class` and `sql_table` shapes. [#982](https://github.com/terrastruct/d2/pull/982)
-- Implements `style.fill-pattern`. Currently only value supported is `dots`. [#1024](https://github.com/terrastruct/d2/pull/1024)
+- Implements `style.fill-pattern`. [#1024](https://github.com/terrastruct/d2/pull/1024), [#1041](https://github.com/terrastruct/d2/pull/1041)
#### Improvements ๐งน
diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go
index f28e8d3f9..8e284f6b6 100644
--- a/d2compiler/compile_test.go
+++ b/d2compiler/compile_test.go
@@ -259,7 +259,7 @@ containers: {
}
}
`,
- expErr: `d2/testdata/d2compiler/TestCompile/invalid-fill-pattern.d2:3:19: expected "fill-pattern" to be one of: dots`,
+ expErr: `d2/testdata/d2compiler/TestCompile/invalid-fill-pattern.d2:3:19: expected "fill-pattern" to be one of: dots, lines, grain`,
},
{
name: "shape_unquoted_hex",
diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go
index 129a2f39a..03c438a58 100644
--- a/d2graph/d2graph.go
+++ b/d2graph/d2graph.go
@@ -204,7 +204,7 @@ func (s *Style) Apply(key, value string) error {
break
}
if !go2.Contains(FillPatterns, strings.ToLower(value)) {
- return fmt.Errorf(`expected "fill-pattern" to be one of: %s`, strings.Join(FillPatterns, ","))
+ return fmt.Errorf(`expected "fill-pattern" to be one of: %s`, strings.Join(FillPatterns, ", "))
}
s.FillPattern.Value = value
case "stroke-width":
@@ -1568,8 +1568,9 @@ var NearConstantsArray = []string{
var NearConstants map[string]struct{}
var FillPatterns = []string{
- // TODO add lined later
"dots",
+ "lines",
+ "grain",
}
// BoardKeywords contains the keywords that create new boards.
diff --git a/d2renderers/d2sketch/sketch.go b/d2renderers/d2sketch/sketch.go
index a77585ea9..63f97c8e6 100644
--- a/d2renderers/d2sketch/sketch.go
+++ b/d2renderers/d2sketch/sketch.go
@@ -362,6 +362,7 @@ func Table(r *Runner, shape d2target.Shape) (string, error) {
pathEl := d2themes.NewThemableElement("path")
pathEl.SetTranslate(float64(shape.Pos.X), float64(shape.Pos.Y))
pathEl.Fill, pathEl.Stroke = d2themes.ShapeTheme(shape)
+ pathEl.FillPattern = shape.FillPattern
pathEl.ClassName = "shape"
pathEl.Style = shape.CSSStyle()
for _, p := range paths {
@@ -388,6 +389,7 @@ func Table(r *Runner, shape d2target.Shape) (string, error) {
pathEl = d2themes.NewThemableElement("path")
pathEl.SetTranslate(float64(shape.Pos.X), float64(shape.Pos.Y))
pathEl.Fill = shape.Fill
+ pathEl.FillPattern = shape.FillPattern
pathEl.ClassName = "class_header"
for _, p := range paths {
pathEl.D = p
@@ -467,6 +469,7 @@ func Table(r *Runner, shape d2target.Shape) (string, error) {
}
pathEl := d2themes.NewThemableElement("path")
pathEl.Fill = shape.Fill
+ pathEl.FillPattern = shape.FillPattern
for _, p := range paths {
pathEl.D = p
output += pathEl.Render()
@@ -501,6 +504,7 @@ func Class(r *Runner, shape d2target.Shape) (string, error) {
pathEl := d2themes.NewThemableElement("path")
pathEl.SetTranslate(float64(shape.Pos.X), float64(shape.Pos.Y))
pathEl.Fill, pathEl.Stroke = d2themes.ShapeTheme(shape)
+ pathEl.FillPattern = shape.FillPattern
pathEl.ClassName = "shape"
pathEl.Style = shape.CSSStyle()
for _, p := range paths {
@@ -528,6 +532,7 @@ func Class(r *Runner, shape d2target.Shape) (string, error) {
pathEl = d2themes.NewThemableElement("path")
pathEl.SetTranslate(float64(shape.Pos.X), float64(shape.Pos.Y))
pathEl.Fill = shape.Fill
+ pathEl.FillPattern = shape.FillPattern
pathEl.ClassName = "class_header"
for _, p := range paths {
pathEl.D = p
@@ -581,6 +586,7 @@ func Class(r *Runner, shape d2target.Shape) (string, error) {
}
pathEl = d2themes.NewThemableElement("path")
pathEl.Fill = shape.Fill
+ pathEl.FillPattern = shape.FillPattern
pathEl.ClassName = "class_header"
for _, p := range paths {
pathEl.D = p
diff --git a/d2renderers/d2sketch/sketch_test.go b/d2renderers/d2sketch/sketch_test.go
index 5d578e019..c95746624 100644
--- a/d2renderers/d2sketch/sketch_test.go
+++ b/d2renderers/d2sketch/sketch_test.go
@@ -1157,7 +1157,27 @@ NETWORK: {
}
}
}
- `,
+D2 Parser: {
+ style.fill-pattern: grain
+ shape: class
+
+ +reader: io.RuneReader
+ # Default visibility is + so no need to specify.
+ readerPos: d2ast.Position
+
+ # Private field.
+ -lookahead: "[]rune"
+
+ # Escape the # to prevent being parsed as comment
+ #lookaheadPos: d2ast.Position
+ # Or just wrap in quotes
+ "#peekn(n int)": (s string, eof bool)
+
+ +peek(): (r rune, eof bool)
+ rewind()
+ commit()
+}
+`,
},
{
name: "dots-3d",
diff --git a/d2renderers/d2sketch/testdata/dots-real/sketch.exp.svg b/d2renderers/d2sketch/testdata/dots-real/sketch.exp.svg
index c901babaa..6af97984a 100644
--- a/d2renderers/d2sketch/testdata/dots-real/sketch.exp.svg
+++ b/d2renderers/d2sketch/testdata/dots-real/sketch.exp.svg
@@ -1,16 +1,16 @@
-
\ No newline at end of file
diff --git a/d2renderers/d2svg/class.go b/d2renderers/d2svg/class.go
index 7a560f225..ff9fb73d7 100644
--- a/d2renderers/d2svg/class.go
+++ b/d2renderers/d2svg/class.go
@@ -16,6 +16,7 @@ func classHeader(diagramHash string, shape d2target.Shape, box *geo.Box, text st
rectEl.X, rectEl.Y = box.TopLeft.X, box.TopLeft.Y
rectEl.Width, rectEl.Height = box.Width, box.Height
rectEl.Fill = shape.Fill
+ rectEl.FillPattern = shape.FillPattern
rectEl.ClassName = "class_header"
if shape.BorderRadius != 0 {
rectEl.ClipPath = fmt.Sprintf("%v-%v", diagramHash, shape.ID)
@@ -91,6 +92,7 @@ func drawClass(writer io.Writer, diagramHash string, targetShape d2target.Shape)
el.Width = float64(targetShape.Width)
el.Height = float64(targetShape.Height)
el.Fill, el.Stroke = d2themes.ShapeTheme(targetShape)
+ el.FillPattern = targetShape.FillPattern
el.Style = targetShape.CSSStyle()
if targetShape.BorderRadius != 0 {
el.Rx = float64(targetShape.BorderRadius)
diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go
index 9e4b823f5..b745bcf95 100644
--- a/d2renderers/d2svg/d2svg.go
+++ b/d2renderers/d2svg/d2svg.go
@@ -62,6 +62,12 @@ var mdCSS string
//go:embed dots.txt
var dots string
+//go:embed lines.txt
+var lines string
+
+//go:embed grain.txt
+var grain string
+
type RenderOpts struct {
Pad int
Sketch bool
@@ -1789,16 +1795,31 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
}
bufStr := buf.String()
- if strings.Contains(bufStr, "dots-overlay") || diagram.Root.FillPattern != "" {
- fmt.Fprint(upperBuf, ``)
fmt.Fprint(upperBuf, "")
- fmt.Fprintf(upperBuf, dots)
+ fmt.Fprintf(upperBuf, patternDefs)
fmt.Fprint(upperBuf, "")
}
diff --git a/d2renderers/d2svg/grain.txt b/d2renderers/d2svg/grain.txt
new file mode 100644
index 000000000..b03186ba5
--- /dev/null
+++ b/d2renderers/d2svg/grain.txt
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/d2renderers/d2svg/lines.txt b/d2renderers/d2svg/lines.txt
new file mode 100644
index 000000000..eab4fad8d
--- /dev/null
+++ b/d2renderers/d2svg/lines.txt
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/d2renderers/d2svg/table.go b/d2renderers/d2svg/table.go
index e159a4155..2707553c4 100644
--- a/d2renderers/d2svg/table.go
+++ b/d2renderers/d2svg/table.go
@@ -45,6 +45,7 @@ func tableHeader(diagramHash string, shape d2target.Shape, box *geo.Box, text st
rectEl.X, rectEl.Y = box.TopLeft.X, box.TopLeft.Y
rectEl.Width, rectEl.Height = box.Width, box.Height
rectEl.Fill = shape.Fill
+ rectEl.FillPattern = shape.FillPattern
rectEl.ClassName = "class_header"
if shape.BorderRadius != 0 {
rectEl.ClipPath = fmt.Sprintf("%v-%v", diagramHash, shape.ID)
@@ -120,6 +121,7 @@ func drawTable(writer io.Writer, diagramHash string, targetShape d2target.Shape)
rectEl.Width = float64(targetShape.Width)
rectEl.Height = float64(targetShape.Height)
rectEl.Fill, rectEl.Stroke = d2themes.ShapeTheme(targetShape)
+ rectEl.FillPattern = targetShape.FillPattern
rectEl.ClassName = "shape"
rectEl.Style = targetShape.CSSStyle()
if targetShape.BorderRadius != 0 {
diff --git a/d2themes/d2themescatalog/catalog.go b/d2themes/d2themescatalog/catalog.go
index 7a8b5e2aa..ee86ac751 100644
--- a/d2themes/d2themescatalog/catalog.go
+++ b/d2themes/d2themescatalog/catalog.go
@@ -23,6 +23,7 @@ var LightCatalog = []d2themes.Theme{
EvergladeGreen,
ButteredToast,
Terminal,
+ TerminalGrayscale,
}
var DarkCatalog = []d2themes.Theme{
diff --git a/d2themes/d2themescatalog/terminal_grayscale.go b/d2themes/d2themescatalog/terminal_grayscale.go
new file mode 100644
index 000000000..08d50eb36
--- /dev/null
+++ b/d2themes/d2themescatalog/terminal_grayscale.go
@@ -0,0 +1,42 @@
+package d2themescatalog
+
+import "oss.terrastruct.com/d2/d2themes"
+
+var TerminalGrayscale = d2themes.Theme{
+ ID: 301,
+ Name: "Terminal Grayscale",
+ Colors: d2themes.ColorPalette{
+ Neutrals: TerminalGrayscaleNeutral,
+
+ B1: "#000410",
+ B2: "#000410",
+ B3: "#FFFFFF",
+ B4: "#E7E9EE",
+ B5: "#F5F6F9",
+ B6: "#FFFFFF",
+
+ AA2: "#6D7284",
+ AA4: "#F5F6F9",
+ AA5: "#FFFFFF",
+
+ AB4: "#F5F6F9",
+ AB5: "#FFFFFF",
+ },
+ SpecialRules: d2themes.SpecialRules{
+ Mono: true,
+ NoCornerRadius: true,
+ OuterContainerDoubleBorder: true,
+ ContainerDots: true,
+ CapsLock: true,
+ },
+}
+
+var TerminalGrayscaleNeutral = d2themes.Neutral{
+ N1: "#000410",
+ N2: "#000410",
+ N3: "#9499AB",
+ N4: "#FFFFFF",
+ N5: "#FFFFFF",
+ N6: "#EEF1F8",
+ N7: "#FFFFFF",
+}
diff --git a/e2etests/patterns_test.go b/e2etests/patterns_test.go
index 754dc2ef1..f57c11a51 100644
--- a/e2etests/patterns_test.go
+++ b/e2etests/patterns_test.go
@@ -150,6 +150,78 @@ NETWORK: {
}
}
}
+`,
+ },
+ {
+ name: "real-lines",
+ script: `
+NETWORK: {
+ style: {
+ stroke: black
+ fill-pattern: lines
+ double-border: true
+ fill: "#E7E9EE"
+ font: mono
+ }
+ CELL TOWER: {
+ style: {
+ stroke: black
+ fill-pattern: lines
+ fill: "#F5F6F9"
+ font: mono
+ }
+ satellites: SATELLITES {
+ shape: stored_data
+ style: {
+ font: mono
+ fill: white
+ stroke: black
+ multiple: true
+ }
+ }
+
+ transmitter: TRANSMITTER {
+ style: {
+ font: mono
+ fill: white
+ stroke: black
+ }
+ }
+
+ satellites -> transmitter: SEND {
+ style.stroke: black
+ style.font: mono
+ }
+ satellites -> transmitter: SEND {
+ style.stroke: black
+ style.font: mono
+ }
+ satellites -> transmitter: SEND {
+ style.stroke: black
+ style.font: mono
+ }
+ }
+}
+
+costumes: {
+ shape: sql_table
+ id: int {constraint: primary_key}
+ silliness: int
+ monster: int
+ last_updated: timestamp
+ style.fill-pattern: lines
+}
+
+monsters: {
+ shape: sql_table
+ id: int {constraint: primary_key}
+ movie: string
+ weight: int
+ last_updated: timestamp
+ style.fill-pattern: grain
+}
+
+costumes.monster -> monsters.id
`,
},
}
diff --git a/e2etests/testdata/patterns/real-lines/dagre/board.exp.json b/e2etests/testdata/patterns/real-lines/dagre/board.exp.json
new file mode 100644
index 000000000..47989de39
--- /dev/null
+++ b/e2etests/testdata/patterns/real-lines/dagre/board.exp.json
@@ -0,0 +1,725 @@
+{
+ "name": "",
+ "isFolderOnly": false,
+ "fontFamily": "SourceSansPro",
+ "shapes": [
+ {
+ "id": "NETWORK",
+ "type": "rectangle",
+ "pos": {
+ "x": 0,
+ "y": 41
+ },
+ "width": 333,
+ "height": 640,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#E7E9EE",
+ "fillPattern": "lines",
+ "stroke": "black",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": true,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "NETWORK",
+ "fontSize": 28,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 116,
+ "labelHeight": 36,
+ "labelPosition": "OUTSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "NETWORK.CELL TOWER",
+ "type": "rectangle",
+ "pos": {
+ "x": 20,
+ "y": 106
+ },
+ "width": 293,
+ "height": 545,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "#F5F6F9",
+ "fillPattern": "lines",
+ "stroke": "black",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "CELL TOWER",
+ "fontSize": 24,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 143,
+ "labelHeight": 31,
+ "labelPosition": "OUTSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "NETWORK.CELL TOWER.satellites",
+ "type": "stored_data",
+ "pos": {
+ "x": 86,
+ "y": 195
+ },
+ "width": 161,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "white",
+ "stroke": "black",
+ "shadow": false,
+ "3d": false,
+ "multiple": true,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "SATELLITES",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 96,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "NETWORK.CELL TOWER.transmitter",
+ "type": "rectangle",
+ "pos": {
+ "x": 91,
+ "y": 496
+ },
+ "width": 151,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "white",
+ "stroke": "black",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "TRANSMITTER",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 106,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "costumes",
+ "type": "sql_table",
+ "pos": {
+ "x": 373,
+ "y": 100
+ },
+ "width": 311,
+ "height": 180,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "N1",
+ "fillPattern": "lines",
+ "stroke": "N7",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": [
+ {
+ "name": {
+ "label": "id",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 15,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "int",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 23,
+ "labelHeight": 26
+ },
+ "constraint": "primary_key",
+ "reference": ""
+ },
+ {
+ "name": {
+ "label": "silliness",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 66,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "int",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 23,
+ "labelHeight": 26
+ },
+ "constraint": "",
+ "reference": ""
+ },
+ {
+ "name": {
+ "label": "monster",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 70,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "int",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 23,
+ "labelHeight": 26
+ },
+ "constraint": "",
+ "reference": ""
+ },
+ {
+ "name": {
+ "label": "last_updated",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 110,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "timestamp",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 91,
+ "labelHeight": 26
+ },
+ "constraint": "",
+ "reference": ""
+ }
+ ],
+ "label": "costumes",
+ "fontSize": 20,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 101,
+ "labelHeight": 31,
+ "zIndex": 0,
+ "level": 1,
+ "primaryAccentColor": "B2",
+ "secondaryAccentColor": "AA2",
+ "neutralAccentColor": "N2"
+ },
+ {
+ "id": "monsters",
+ "type": "sql_table",
+ "pos": {
+ "x": 373,
+ "y": 401
+ },
+ "width": 311,
+ "height": 180,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "N1",
+ "fillPattern": "grain",
+ "stroke": "N7",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": [
+ {
+ "name": {
+ "label": "id",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 15,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "int",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 23,
+ "labelHeight": 26
+ },
+ "constraint": "primary_key",
+ "reference": ""
+ },
+ {
+ "name": {
+ "label": "movie",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 51,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "string",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 48,
+ "labelHeight": 26
+ },
+ "constraint": "",
+ "reference": ""
+ },
+ {
+ "name": {
+ "label": "weight",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 58,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "int",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 23,
+ "labelHeight": 26
+ },
+ "constraint": "",
+ "reference": ""
+ },
+ {
+ "name": {
+ "label": "last_updated",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 110,
+ "labelHeight": 26
+ },
+ "type": {
+ "label": "timestamp",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 91,
+ "labelHeight": 26
+ },
+ "constraint": "",
+ "reference": ""
+ }
+ ],
+ "label": "monsters",
+ "fontSize": 20,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 99,
+ "labelHeight": 31,
+ "zIndex": 0,
+ "level": 1,
+ "primaryAccentColor": "B2",
+ "secondaryAccentColor": "AA2",
+ "neutralAccentColor": "N2"
+ }
+ ],
+ "connections": [
+ {
+ "id": "NETWORK.CELL TOWER.(satellites -> transmitter)[0]",
+ "src": "NETWORK.CELL TOWER.satellites",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "NETWORK.CELL TOWER.transmitter",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "black",
+ "borderRadius": 10,
+ "label": "SEND",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 37,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 151,
+ "y": 262
+ },
+ {
+ "x": 107.8,
+ "y": 355.6
+ },
+ {
+ "x": 107.8,
+ "y": 402.6
+ },
+ {
+ "x": 151,
+ "y": 497
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "NETWORK.CELL TOWER.(satellites -> transmitter)[1]",
+ "src": "NETWORK.CELL TOWER.satellites",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "NETWORK.CELL TOWER.transmitter",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "black",
+ "borderRadius": 10,
+ "label": "SEND",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 37,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 166,
+ "y": 262
+ },
+ {
+ "x": 166.4,
+ "y": 355.6
+ },
+ {
+ "x": 166.5,
+ "y": 402.6
+ },
+ {
+ "x": 166.5,
+ "y": 497
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "NETWORK.CELL TOWER.(satellites -> transmitter)[2]",
+ "src": "NETWORK.CELL TOWER.satellites",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "NETWORK.CELL TOWER.transmitter",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "black",
+ "borderRadius": 10,
+ "label": "SEND",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 37,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 182,
+ "y": 262
+ },
+ {
+ "x": 225.2,
+ "y": 355.6
+ },
+ {
+ "x": 225.2,
+ "y": 402.6
+ },
+ {
+ "x": 182,
+ "y": 497
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(costumes -> monsters)[0]",
+ "src": "costumes",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "monsters",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "borderRadius": 10,
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "DEFAULT",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 528.5,
+ "y": 280
+ },
+ {
+ "x": 528.5,
+ "y": 328.4
+ },
+ {
+ "x": 528.5,
+ "y": 352.7
+ },
+ {
+ "x": 528.5,
+ "y": 401.5
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ],
+ "root": {
+ "id": "",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "opacity": 0,
+ "strokeDash": 0,
+ "strokeWidth": 0,
+ "borderRadius": 0,
+ "fill": "N7",
+ "stroke": "",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "zIndex": 0,
+ "level": 0
+ }
+}
diff --git a/e2etests/testdata/patterns/real-lines/dagre/sketch.exp.svg b/e2etests/testdata/patterns/real-lines/dagre/sketch.exp.svg
new file mode 100644
index 000000000..bd78a6981
--- /dev/null
+++ b/e2etests/testdata/patterns/real-lines/dagre/sketch.exp.svg
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+NETWORKcostumesidintPKsillinessintmonsterintlast_updatedtimestampmonstersidintPKmoviestringweightintlast_updatedtimestampCELL TOWERSATELLITESTRANSMITTER SENDSENDSEND
+
+
+
+
+
\ No newline at end of file
diff --git a/e2etests/testdata/patterns/root-dots-with-fill/dagre/sketch.exp.svg b/e2etests/testdata/patterns/root-dots-with-fill/dagre/sketch.exp.svg
index 144ec669e..541f61982 100644
--- a/e2etests/testdata/patterns/root-dots-with-fill/dagre/sketch.exp.svg
+++ b/e2etests/testdata/patterns/root-dots-with-fill/dagre/sketch.exp.svg
@@ -93,6 +93,14 @@
.dots-overlay {
fill: url(#dots);
mix-blend-mode: multiply;
+}
+.lines-overlay {
+ fill: url(#lines);
+ mix-blend-mode: multiply;
+}
+.grain-overlay {
+ fill: url(#grain);
+ mix-blend-mode: multiply;
}]]>
@@ -122,6 +130,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
xyzabcdg
diff --git a/e2etests/testdata/patterns/root-dots/dagre/sketch.exp.svg b/e2etests/testdata/patterns/root-dots/dagre/sketch.exp.svg
index bc0dda57c..3f445c86e 100644
--- a/e2etests/testdata/patterns/root-dots/dagre/sketch.exp.svg
+++ b/e2etests/testdata/patterns/root-dots/dagre/sketch.exp.svg
@@ -93,6 +93,14 @@
.dots-overlay {
fill: url(#dots);
mix-blend-mode: multiply;
+}
+.lines-overlay {
+ fill: url(#lines);
+ mix-blend-mode: multiply;
+}
+.grain-overlay {
+ fill: url(#grain);
+ mix-blend-mode: multiply;
}]]>
@@ -122,6 +130,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
xyzabcdg
diff --git a/e2etests/testdata/themes/terminal_grayscale/dagre/board.exp.json b/e2etests/testdata/themes/terminal_grayscale/dagre/board.exp.json
new file mode 100644
index 000000000..aaff0b214
--- /dev/null
+++ b/e2etests/testdata/themes/terminal_grayscale/dagre/board.exp.json
@@ -0,0 +1,1104 @@
+{
+ "name": "",
+ "isFolderOnly": false,
+ "fontFamily": "SourceCodePro",
+ "shapes": [
+ {
+ "id": "network",
+ "type": "rectangle",
+ "pos": {
+ "x": 0,
+ "y": 275
+ },
+ "width": 410,
+ "height": 1225,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B4",
+ "fillPattern": "dots",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": true,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "NETWORK",
+ "fontSize": 28,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 116,
+ "labelHeight": 36,
+ "labelPosition": "OUTSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "network.cell tower",
+ "type": "rectangle",
+ "pos": {
+ "x": 96,
+ "y": 340
+ },
+ "width": 294,
+ "height": 317,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "fillPattern": "dots",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "CELL TOWER",
+ "fontSize": 24,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 143,
+ "labelHeight": 31,
+ "labelPosition": "OUTSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "network.cell tower.satellites",
+ "type": "stored_data",
+ "pos": {
+ "x": 163,
+ "y": 372
+ },
+ "width": 161,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "AA5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": true,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "SATELLITES",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 96,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "network.cell tower.transmitter",
+ "type": "rectangle",
+ "pos": {
+ "x": 168,
+ "y": 559
+ },
+ "width": 151,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "TRANSMITTER",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 106,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "network.online portal",
+ "type": "rectangle",
+ "pos": {
+ "x": 20,
+ "y": 1319
+ },
+ "width": 157,
+ "height": 151,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "fillPattern": "dots",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "ONLINE PORTAL",
+ "fontSize": 24,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 185,
+ "labelHeight": 31,
+ "labelPosition": "OUTSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "network.online portal.ui",
+ "type": "hexagon",
+ "pos": {
+ "x": 71,
+ "y": 1360
+ },
+ "width": 65,
+ "height": 69,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "N5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "UI",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 18,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "network.data processor",
+ "type": "rectangle",
+ "pos": {
+ "x": 147,
+ "y": 814
+ },
+ "width": 192,
+ "height": 182,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "fillPattern": "dots",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "DATA PROCESSOR",
+ "fontSize": 24,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 200,
+ "labelHeight": 31,
+ "labelPosition": "OUTSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "network.data processor.storage",
+ "type": "cylinder",
+ "pos": {
+ "x": 187,
+ "y": 846
+ },
+ "width": 112,
+ "height": 118,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "AA5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": true,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "STORAGE",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 67,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "user",
+ "type": "person",
+ "pos": {
+ "x": 82,
+ "y": 0
+ },
+ "width": 130,
+ "height": 87,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B3",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "USER",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 38,
+ "labelHeight": 21,
+ "labelPosition": "OUTSIDE_BOTTOM_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "api server",
+ "type": "rectangle",
+ "pos": {
+ "x": 450,
+ "y": 1076
+ },
+ "width": 142,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "API SERVER",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 97,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "logs",
+ "type": "page",
+ "pos": {
+ "x": 480,
+ "y": 1313
+ },
+ "width": 82,
+ "height": 87,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "AB4",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": true,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "LOGS",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 37,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ }
+ ],
+ "connections": [
+ {
+ "id": "network.cell tower.(satellites -> transmitter)[0]",
+ "src": "network.cell tower.satellites",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "network.cell tower.transmitter",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "SEND",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 38,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 218,
+ "y": 439
+ },
+ {
+ "x": 182.4,
+ "y": 487
+ },
+ {
+ "x": 182.5,
+ "y": 511.2
+ },
+ {
+ "x": 218.5,
+ "y": 560
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "network.cell tower.(satellites -> transmitter)[1]",
+ "src": "network.cell tower.satellites",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "network.cell tower.transmitter",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "SEND",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 38,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 243,
+ "y": 439
+ },
+ {
+ "x": 243.2,
+ "y": 487
+ },
+ {
+ "x": 243.25,
+ "y": 511.2
+ },
+ {
+ "x": 243.25,
+ "y": 560
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "network.cell tower.(satellites -> transmitter)[2]",
+ "src": "network.cell tower.satellites",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "network.cell tower.transmitter",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "SEND",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 38,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 268,
+ "y": 439
+ },
+ {
+ "x": 304,
+ "y": 487
+ },
+ {
+ "x": 304,
+ "y": 511.2
+ },
+ {
+ "x": 268,
+ "y": 560
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "network.(cell tower.transmitter -> data processor.storage)[0]",
+ "src": "network.cell tower.transmitter",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "network.data processor.storage",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "PHONE LOGS",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 96,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 243.25,
+ "y": 625.5
+ },
+ {
+ "x": 243.25,
+ "y": 651.1
+ },
+ {
+ "x": 243.25,
+ "y": 669.6
+ },
+ {
+ "x": 243.25,
+ "y": 687.75
+ },
+ {
+ "x": 243.25,
+ "y": 705.9
+ },
+ {
+ "x": 243.2,
+ "y": 792.2
+ },
+ {
+ "x": 243,
+ "y": 847
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(user -> network.cell tower)[0]",
+ "src": "user",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "network.cell tower",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "MAKE CALL",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 86,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 172,
+ "y": 87
+ },
+ {
+ "x": 229,
+ "y": 156.2
+ },
+ {
+ "x": 243.25,
+ "y": 248.2
+ },
+ {
+ "x": 243.25,
+ "y": 305
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(user -> network.online portal.ui)[0]",
+ "src": "user",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "network.online portal.ui",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 3,
+ "strokeWidth": 2,
+ "stroke": "B2",
+ "label": "ACCESS",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 58,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 128,
+ "y": 87
+ },
+ {
+ "x": 86.6,
+ "y": 156.2
+ },
+ {
+ "x": 76.25,
+ "y": 185.6
+ },
+ {
+ "x": 76.25,
+ "y": 203.75
+ },
+ {
+ "x": 76.25,
+ "y": 221.9
+ },
+ {
+ "x": 76.25,
+ "y": 244
+ },
+ {
+ "x": 76.25,
+ "y": 259
+ },
+ {
+ "x": 76.25,
+ "y": 274
+ },
+ {
+ "x": 76.25,
+ "y": 300.6
+ },
+ {
+ "x": 76.25,
+ "y": 325.5
+ },
+ {
+ "x": 76.25,
+ "y": 350.4
+ },
+ {
+ "x": 76.25,
+ "y": 385.7
+ },
+ {
+ "x": 76.25,
+ "y": 413.75
+ },
+ {
+ "x": 76.25,
+ "y": 441.8
+ },
+ {
+ "x": 76.25,
+ "y": 479.2
+ },
+ {
+ "x": 76.25,
+ "y": 507.25
+ },
+ {
+ "x": 76.25,
+ "y": 535.3
+ },
+ {
+ "x": 76.25,
+ "y": 570.6
+ },
+ {
+ "x": 76.25,
+ "y": 595.5
+ },
+ {
+ "x": 76.25,
+ "y": 620.4
+ },
+ {
+ "x": 76.25,
+ "y": 649.1
+ },
+ {
+ "x": 76.25,
+ "y": 667.25
+ },
+ {
+ "x": 76.25,
+ "y": 685.4
+ },
+ {
+ "x": 76.25,
+ "y": 709.6
+ },
+ {
+ "x": 76.25,
+ "y": 727.75
+ },
+ {
+ "x": 76.25,
+ "y": 745.9
+ },
+ {
+ "x": 76.25,
+ "y": 779.8
+ },
+ {
+ "x": 76.25,
+ "y": 812.5
+ },
+ {
+ "x": 76.25,
+ "y": 845.2
+ },
+ {
+ "x": 76.25,
+ "y": 888.8
+ },
+ {
+ "x": 76.25,
+ "y": 921.5
+ },
+ {
+ "x": 76.25,
+ "y": 954.2
+ },
+ {
+ "x": 76.25,
+ "y": 986
+ },
+ {
+ "x": 76.25,
+ "y": 1001
+ },
+ {
+ "x": 76.25,
+ "y": 1016
+ },
+ {
+ "x": 76.25,
+ "y": 1042.6
+ },
+ {
+ "x": 76.25,
+ "y": 1067.5
+ },
+ {
+ "x": 76.25,
+ "y": 1092.4
+ },
+ {
+ "x": 76.25,
+ "y": 1127.7
+ },
+ {
+ "x": 76.25,
+ "y": 1155.75
+ },
+ {
+ "x": 76.25,
+ "y": 1183.8
+ },
+ {
+ "x": 79.6,
+ "y": 1282.6
+ },
+ {
+ "x": 93,
+ "y": 1361
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(api server -> network.online portal.ui)[0]",
+ "src": "api server",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "network.online portal.ui",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "DISPLAY",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 69,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 450.25,
+ "y": 1126
+ },
+ {
+ "x": 194.64999999999998,
+ "y": 1187.2
+ },
+ {
+ "x": 127.4,
+ "y": 1282.6
+ },
+ {
+ "x": 114,
+ "y": 1361
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(api server -> logs)[0]",
+ "src": "api server",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "logs",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "PERSIST",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 68,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 521.25,
+ "y": 1142
+ },
+ {
+ "x": 521.25,
+ "y": 1190.4
+ },
+ {
+ "x": 521.2,
+ "y": 1273
+ },
+ {
+ "x": 521,
+ "y": 1313
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(network.data processor -> api server)[0]",
+ "src": "network.data processor",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "api server",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 243.25,
+ "y": 996.5
+ },
+ {
+ "x": 243.25,
+ "y": 1020.1
+ },
+ {
+ "x": 284.65,
+ "y": 1038.4
+ },
+ {
+ "x": 450.25,
+ "y": 1088
+ }
+ ],
+ "isCurve": true,
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ],
+ "root": {
+ "id": "",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "opacity": 0,
+ "strokeDash": 0,
+ "strokeWidth": 0,
+ "borderRadius": 0,
+ "fill": "N7",
+ "stroke": "",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "zIndex": 0,
+ "level": 0
+ }
+}
diff --git a/e2etests/testdata/themes/terminal_grayscale/dagre/sketch.exp.svg b/e2etests/testdata/themes/terminal_grayscale/dagre/sketch.exp.svg
new file mode 100644
index 000000000..ce286b314
--- /dev/null
+++ b/e2etests/testdata/themes/terminal_grayscale/dagre/sketch.exp.svg
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+NETWORKUSERAPI SERVERLOGSCELL TOWERONLINE PORTALDATA PROCESSORSATELLITESTRANSMITTERUISTORAGE SENDSENDSENDPHONE LOGSMAKE CALL ACCESSDISPLAYPERSIST
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2etests/testdata/themes/terminal_grayscale/elk/board.exp.json b/e2etests/testdata/themes/terminal_grayscale/elk/board.exp.json
new file mode 100644
index 000000000..b673875c3
--- /dev/null
+++ b/e2etests/testdata/themes/terminal_grayscale/elk/board.exp.json
@@ -0,0 +1,895 @@
+{
+ "name": "",
+ "isFolderOnly": false,
+ "fontFamily": "SourceCodePro",
+ "shapes": [
+ {
+ "id": "network",
+ "type": "rectangle",
+ "pos": {
+ "x": 12,
+ "y": 311
+ },
+ "width": 611,
+ "height": 902,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B4",
+ "fillPattern": "dots",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": true,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "NETWORK",
+ "fontSize": 28,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 116,
+ "labelHeight": 36,
+ "labelPosition": "INSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "network.cell tower",
+ "type": "rectangle",
+ "pos": {
+ "x": 62,
+ "y": 361
+ },
+ "width": 261,
+ "height": 413,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "fillPattern": "dots",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "CELL TOWER",
+ "fontSize": 24,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 143,
+ "labelHeight": 31,
+ "labelPosition": "INSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "network.cell tower.satellites",
+ "type": "stored_data",
+ "pos": {
+ "x": 112,
+ "y": 411
+ },
+ "width": 161,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "AA5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": true,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "SATELLITES",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 96,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "network.cell tower.transmitter",
+ "type": "rectangle",
+ "pos": {
+ "x": 117,
+ "y": 658
+ },
+ "width": 151,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "TRANSMITTER",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 106,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "network.online portal",
+ "type": "rectangle",
+ "pos": {
+ "x": 343,
+ "y": 366
+ },
+ "width": 230,
+ "height": 169,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "fillPattern": "dots",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "ONLINE PORTAL",
+ "fontSize": 24,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 185,
+ "labelHeight": 31,
+ "labelPosition": "INSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "network.online portal.ui",
+ "type": "hexagon",
+ "pos": {
+ "x": 418,
+ "y": 416
+ },
+ "width": 80,
+ "height": 69,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "N5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "UI",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 18,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "network.data processor",
+ "type": "rectangle",
+ "pos": {
+ "x": 70,
+ "y": 945
+ },
+ "width": 245,
+ "height": 218,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B5",
+ "fillPattern": "dots",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "DATA PROCESSOR",
+ "fontSize": 24,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 200,
+ "labelHeight": 31,
+ "labelPosition": "INSIDE_TOP_CENTER",
+ "zIndex": 0,
+ "level": 2
+ },
+ {
+ "id": "network.data processor.storage",
+ "type": "cylinder",
+ "pos": {
+ "x": 136,
+ "y": 995
+ },
+ "width": 112,
+ "height": 118,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "AA5",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": true,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "STORAGE",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 67,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 3
+ },
+ {
+ "id": "user",
+ "type": "person",
+ "pos": {
+ "x": 314,
+ "y": 12
+ },
+ "width": 130,
+ "height": 87,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B3",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "USER",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 38,
+ "labelHeight": 21,
+ "labelPosition": "OUTSIDE_BOTTOM_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "api server",
+ "type": "rectangle",
+ "pos": {
+ "x": 592,
+ "y": 59
+ },
+ "width": 142,
+ "height": 66,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "B6",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "API SERVER",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 97,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ },
+ {
+ "id": "logs",
+ "type": "page",
+ "pos": {
+ "x": 703,
+ "y": 311
+ },
+ "width": 82,
+ "height": 87,
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "borderRadius": 0,
+ "fill": "AB4",
+ "stroke": "B1",
+ "shadow": false,
+ "3d": false,
+ "multiple": true,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "LOGS",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N1",
+ "italic": false,
+ "bold": true,
+ "underline": false,
+ "labelWidth": 37,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "zIndex": 0,
+ "level": 1
+ }
+ ],
+ "connections": [
+ {
+ "id": "network.cell tower.(satellites -> transmitter)[0]",
+ "src": "network.cell tower.satellites",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "network.cell tower.transmitter",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "SEND",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 38,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 143,
+ "y": 477
+ },
+ {
+ "x": 143.5,
+ "y": 658
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "network.cell tower.(satellites -> transmitter)[1]",
+ "src": "network.cell tower.satellites",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "network.cell tower.transmitter",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "SEND",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 38,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 192,
+ "y": 477
+ },
+ {
+ "x": 192.5,
+ "y": 658
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "network.cell tower.(satellites -> transmitter)[2]",
+ "src": "network.cell tower.satellites",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "network.cell tower.transmitter",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "SEND",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 38,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 241,
+ "y": 477
+ },
+ {
+ "x": 241.5,
+ "y": 658
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "network.(cell tower.transmitter -> data processor.storage)[0]",
+ "src": "network.cell tower.transmitter",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "network.data processor.storage",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "PHONE LOGS",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 96,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 192.5,
+ "y": 724
+ },
+ {
+ "x": 193,
+ "y": 995
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(user -> network.cell tower)[0]",
+ "src": "user",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "network.cell tower",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "MAKE CALL",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 86,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 358,
+ "y": 99
+ },
+ {
+ "x": 357.58333333333337,
+ "y": 165
+ },
+ {
+ "x": 236.83333333333334,
+ "y": 165
+ },
+ {
+ "x": 236.83333333333334,
+ "y": 361
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(user -> network.online portal.ui)[0]",
+ "src": "user",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "network.online portal.ui",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 3,
+ "strokeWidth": 2,
+ "stroke": "B2",
+ "label": "ACCESS",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 58,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 401,
+ "y": 99
+ },
+ {
+ "x": 400.9166666666667,
+ "y": 165
+ },
+ {
+ "x": 444.6666666666667,
+ "y": 165
+ },
+ {
+ "x": 445,
+ "y": 416
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(api server -> network.online portal.ui)[0]",
+ "src": "api server",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "network.online portal.ui",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "DISPLAY",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 69,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 627.5,
+ "y": 125
+ },
+ {
+ "x": 627.5,
+ "y": 266
+ },
+ {
+ "x": 471.33333333333337,
+ "y": 266
+ },
+ {
+ "x": 471,
+ "y": 416
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(api server -> logs)[0]",
+ "src": "api server",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "logs",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "PERSIST",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 68,
+ "labelHeight": 21,
+ "labelPosition": "INSIDE_MIDDLE_CENTER",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 698.5,
+ "y": 125
+ },
+ {
+ "x": 698.5,
+ "y": 165
+ },
+ {
+ "x": 744,
+ "y": 165
+ },
+ {
+ "x": 744,
+ "y": 311
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ },
+ {
+ "id": "(network.data processor -> api server)[0]",
+ "src": "network.data processor",
+ "srcArrow": "none",
+ "srcLabel": "",
+ "dst": "api server",
+ "dstArrow": "triangle",
+ "dstLabel": "",
+ "opacity": 1,
+ "strokeDash": 0,
+ "strokeWidth": 2,
+ "stroke": "B1",
+ "label": "",
+ "fontSize": 16,
+ "fontFamily": "mono",
+ "language": "",
+ "color": "N2",
+ "italic": true,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "labelPosition": "",
+ "labelPercentage": 0,
+ "route": [
+ {
+ "x": 136.5,
+ "y": 1163
+ },
+ {
+ "x": 136.5,
+ "y": 1258
+ },
+ {
+ "x": 663,
+ "y": 1258
+ },
+ {
+ "x": 663,
+ "y": 125
+ }
+ ],
+ "animated": false,
+ "tooltip": "",
+ "icon": null,
+ "zIndex": 0
+ }
+ ],
+ "root": {
+ "id": "",
+ "type": "",
+ "pos": {
+ "x": 0,
+ "y": 0
+ },
+ "width": 0,
+ "height": 0,
+ "opacity": 0,
+ "strokeDash": 0,
+ "strokeWidth": 0,
+ "borderRadius": 0,
+ "fill": "N7",
+ "stroke": "",
+ "shadow": false,
+ "3d": false,
+ "multiple": false,
+ "double-border": false,
+ "tooltip": "",
+ "link": "",
+ "icon": null,
+ "iconPosition": "",
+ "blend": false,
+ "fields": null,
+ "methods": null,
+ "columns": null,
+ "label": "",
+ "fontSize": 0,
+ "fontFamily": "",
+ "language": "",
+ "color": "",
+ "italic": false,
+ "bold": false,
+ "underline": false,
+ "labelWidth": 0,
+ "labelHeight": 0,
+ "zIndex": 0,
+ "level": 0
+ }
+}
diff --git a/e2etests/testdata/themes/terminal_grayscale/elk/sketch.exp.svg b/e2etests/testdata/themes/terminal_grayscale/elk/sketch.exp.svg
new file mode 100644
index 000000000..3637dcb5f
--- /dev/null
+++ b/e2etests/testdata/themes/terminal_grayscale/elk/sketch.exp.svg
@@ -0,0 +1,142 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+NETWORKUSERAPI SERVERLOGSCELL TOWERONLINE PORTALDATA PROCESSORSATELLITESTRANSMITTERUISTORAGE SENDSENDSENDPHONE LOGSMAKE CALL ACCESSDISPLAYPERSIST
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/e2etests/themes_test.go b/e2etests/themes_test.go
index 47c384956..3f02a222a 100644
--- a/e2etests/themes_test.go
+++ b/e2etests/themes_test.go
@@ -55,6 +55,55 @@ api server -> network.online portal.ui: display
api server -> logs: persist
logs: { shape: page; style.multiple: true }
+network.data processor -> api server
+`,
+ },
+ {
+ name: "terminal_grayscale",
+ themeID: d2themescatalog.TerminalGrayscale.ID,
+ script: `
+network: {
+ cell tower: {
+ satellites: {
+ shape: stored_data
+ style.multiple: true
+ }
+
+ transmitter
+
+ satellites -> transmitter: send
+ satellites -> transmitter: send
+ satellites -> transmitter: send
+ }
+
+ online portal: {
+ ui: { shape: hexagon }
+ }
+
+ data processor: {
+ storage: {
+ shape: cylinder
+ style.multiple: true
+ }
+ }
+
+ cell tower.transmitter -> data processor.storage: phone logs
+}
+
+user: {
+ shape: person
+ width: 130
+}
+
+user -> network.cell tower: make call
+user -> network.online portal.ui: access {
+ style.stroke-dash: 3
+}
+
+api server -> network.online portal.ui: display
+api server -> logs: persist
+logs: { shape: page; style.multiple: true }
+
network.data processor -> api server
`,
},
diff --git a/testdata/d2compiler/TestCompile/invalid-fill-pattern.exp.json b/testdata/d2compiler/TestCompile/invalid-fill-pattern.exp.json
index 3bd358bf9..7d8ea713e 100644
--- a/testdata/d2compiler/TestCompile/invalid-fill-pattern.exp.json
+++ b/testdata/d2compiler/TestCompile/invalid-fill-pattern.exp.json
@@ -5,7 +5,7 @@
"errs": [
{
"range": "d2/testdata/d2compiler/TestCompile/invalid-fill-pattern.d2,2:18:33-2:23:38",
- "errmsg": "d2/testdata/d2compiler/TestCompile/invalid-fill-pattern.d2:3:19: expected \"fill-pattern\" to be one of: dots"
+ "errmsg": "d2/testdata/d2compiler/TestCompile/invalid-fill-pattern.d2:3:19: expected \"fill-pattern\" to be one of: dots, lines, grain"
}
]
}