Merge branch 'terrastruct:master' into fix-bugs

This commit is contained in:
Fuad Hasan 2025-02-04 20:04:27 +06:00 committed by GitHub
commit e63051cb0d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 4902 additions and 555 deletions

View file

@ -261,6 +261,7 @@ let us know and we'll be happy to include it here!
- **ent2d2**: [https://github.com/tmc/ent2d2](https://github.com/tmc/ent2d2) - **ent2d2**: [https://github.com/tmc/ent2d2](https://github.com/tmc/ent2d2)
- **MkDocs Plugin**: [https://github.com/landmaj/mkdocs-d2-plugin](https://github.com/landmaj/mkdocs-d2-plugin) - **MkDocs Plugin**: [https://github.com/landmaj/mkdocs-d2-plugin](https://github.com/landmaj/mkdocs-d2-plugin)
- **Remark Plugin**: [https://github.com/mech-a/remark-d2](https://github.com/mech-a/remark-d2) - **Remark Plugin**: [https://github.com/mech-a/remark-d2](https://github.com/mech-a/remark-d2)
- **VitePress Plugin**: [https://github.com/BadgerHobbs/vitepress-plugin-d2](https://github.com/BadgerHobbs/vitepress-plugin-d2)
- **Zed extension**: [https://github.com/gabeidx/zed-d2](https://github.com/gabeidx/zed-d2) - **Zed extension**: [https://github.com/gabeidx/zed-d2](https://github.com/gabeidx/zed-d2)
### Misc ### Misc

View file

@ -4,10 +4,12 @@
- Connections now support `link` [#1955](https://github.com/terrastruct/d2/pull/1955) - Connections now support `link` [#1955](https://github.com/terrastruct/d2/pull/1955)
- Vars: vars in markdown blocks are substituted [#2218](https://github.com/terrastruct/d2/pull/2218) - Vars: vars in markdown blocks are substituted [#2218](https://github.com/terrastruct/d2/pull/2218)
- Markdown: Github-flavored tables work in `md` blocks [#2221](https://github.com/terrastruct/d2/pull/2221) - Markdown: Github-flavored tables work in `md` blocks [#2221](https://github.com/terrastruct/d2/pull/2221)
- Render: adds box arrowheads [#2227](https://github.com/terrastruct/d2/issues/2227)
- `d2 fmt` now supports a `--check` flag [#2253](https://github.com/terrastruct/d2/pull/2253) - `d2 fmt` now supports a `--check` flag [#2253](https://github.com/terrastruct/d2/pull/2253)
- CLI: PNG output to stdout is supported using `--stdout-format png -` [#2291](https://github.com/terrastruct/d2/pull/2291) - CLI: PNG output to stdout is supported using `--stdout-format png -` [#2291](https://github.com/terrastruct/d2/pull/2291)
- Globs: `&connected` and `&leaf` filters are implemented [#2299](https://github.com/terrastruct/d2/pull/2299) - Globs: `&connected` and `&leaf` filters are implemented [#2299](https://github.com/terrastruct/d2/pull/2299)
- CLI: add --no-xml-tag for direct HTML embedding [#2302](https://github.com/terrastruct/d2/pull/2302) - CLI: add --no-xml-tag for direct HTML embedding [#2302](https://github.com/terrastruct/d2/pull/2302)
- CLI: `play` cmd added for opening d2 input in online playground [#2242](https://github.com/terrastruct/d2/pull/2242)
#### Improvements 🧹 #### Improvements 🧹

View file

@ -15,6 +15,8 @@
.Ar layout Op Ar name .Ar layout Op Ar name
.Nm d2 .Nm d2
.Ar fmt Ar file.d2 ... .Ar fmt Ar file.d2 ...
.Nm d2
.Ar play Ar file.d2
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
compiles and renders compiles and renders
@ -141,6 +143,9 @@ Print version information and exit
.It Fl -stdout-format Ar string .It Fl -stdout-format Ar string
Set the output format when writing to stdout. Supported formats are: png, svg. Only used when output is set to stdout (-) Set the output format when writing to stdout. Supported formats are: png, svg. Only used when output is set to stdout (-)
.Ns . .Ns .
.It Fl -no-xml-tag Ar false
Omit XML tag (<?xml ...?>) from output SVG files. Useful when generating SVGs for direct HTML embedding
.Ns .
.El .El
.Sh SUBCOMMANDS .Sh SUBCOMMANDS
.Bl -tag -width Fl .Bl -tag -width Fl
@ -155,10 +160,8 @@ Lists available themes
.Ns . .Ns .
.It Ar fmt Ar file.d2 ... .It Ar fmt Ar file.d2 ...
Format all passed files Format all passed files
.It Fl -no-xml-tag Ar false .It Ar play Ar file.d2
Omit XML tag (<?xml ...?>) from output SVG files. Useful when generating SVGs for direct HTML embedding Opens the file in playground, an online web viewer (https://play.d2lang.com)
.Ns .
.Ns .
.El .El
.Sh ENVIRONMENT VARIABLES .Sh ENVIRONMENT VARIABLES
Many flags can also be set with environment variables. Many flags can also be set with environment variables.

View file

@ -22,6 +22,7 @@ Usage:
%[1]s [--watch=false] [--theme=0] file.d2 [file.svg | file.png] %[1]s [--watch=false] [--theme=0] file.d2 [file.svg | file.png]
%[1]s layout [name] %[1]s layout [name]
%[1]s fmt file.d2 ... %[1]s fmt file.d2 ...
%[1]s play [--theme=0] [--sketch] file.d2
%[1]s compiles and renders file.d2 to file.svg | file.png %[1]s compiles and renders file.d2 to file.svg | file.png
It defaults to file.svg if an output path is not provided. It defaults to file.svg if an output path is not provided.
@ -38,6 +39,7 @@ Subcommands:
%[1]s layout [name] - Display long help for a particular layout engine, including its configuration options %[1]s layout [name] - Display long help for a particular layout engine, including its configuration options
%[1]s themes - Lists available themes %[1]s themes - Lists available themes
%[1]s fmt file.d2 ... - Format passed files %[1]s fmt file.d2 ... - Format passed files
%[1]s play file.d2 - Opens the file in playground, an online web viewer (https://play.d2lang.com)
See more docs and the source code at https://oss.terrastruct.com/d2. See more docs and the source code at https://oss.terrastruct.com/d2.
Hosted icons at https://icons.terrastruct.com. Hosted icons at https://icons.terrastruct.com.

View file

@ -171,6 +171,8 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
return nil return nil
case "fmt": case "fmt":
return fmtCmd(ctx, ms, *checkFlag) return fmtCmd(ctx, ms, *checkFlag)
case "play":
return playCmd(ctx, ms)
case "version": case "version":
if len(ms.Opts.Flags.Args()) > 1 { if len(ms.Opts.Flags.Args()) > 1 {
return xmain.UsageErrorf("version subcommand accepts no arguments") return xmain.UsageErrorf("version subcommand accepts no arguments")

75
d2cli/play.go Normal file
View file

@ -0,0 +1,75 @@
package d2cli
import (
"context"
"fmt"
"io"
"os"
"oss.terrastruct.com/d2/lib/urlenc"
"oss.terrastruct.com/util-go/xbrowser"
"oss.terrastruct.com/util-go/xmain"
)
func playCmd(ctx context.Context, ms *xmain.State) error {
if len(ms.Opts.Flags.Args()) != 2 {
return xmain.UsageErrorf("play must be passed one argument: either a filepath or '-' for stdin")
}
filepath := ms.Opts.Flags.Args()[1]
theme, err := ms.Opts.Flags.GetInt64("theme")
if err != nil {
return err
}
sketch, err := ms.Opts.Flags.GetBool("sketch")
if err != nil {
return err
}
var sketchNumber int
if sketch {
sketchNumber = 1
} else {
sketchNumber = 0
}
fileRaw, err := readInput(filepath)
if err != nil {
return err
}
encoded, err := urlenc.Encode(fileRaw)
if err != nil {
return err
}
url := fmt.Sprintf("https://play.d2lang.com/?script=%s&sketch=%d&theme=%d&", encoded, sketchNumber, theme)
openBrowser(ctx, ms, url)
return nil
}
func readInput(filepath string) (string, error) {
if filepath == "-" {
data, err := io.ReadAll(os.Stdin)
if err != nil {
return "", fmt.Errorf("error reading from stdin: %w", err)
}
return string(data), nil
}
data, err := os.ReadFile(filepath)
if err != nil {
return "", xmain.UsageErrorf(err.Error())
}
return string(data), nil
}
func openBrowser(ctx context.Context, ms *xmain.State, url string) {
ms.Log.Info.Printf("opening playground: %s", url)
err := xbrowser.Open(ctx, ms.Env, url)
if err != nil {
ms.Log.Warn.Printf("failed to open browser to %v: %v", url, err)
}
}

View file

@ -840,6 +840,22 @@ func ArrowheadJS(r jsrunner.JSRunner, arrowhead d2target.Arrowhead, stroke strin
stroke, stroke,
BG_COLOR, BG_COLOR,
) )
case d2target.BoxArrowhead:
arrowJS = fmt.Sprintf(
`node = rc.polygon(%s, { strokeWidth: %d, stroke: "%s", fill: "%s", fillStyle: "solid", seed: 1})`,
`[[0, -10], [0, 10], [-20, 10], [-20, -10]]`,
strokeWidth,
stroke,
BG_COLOR,
)
case d2target.FilledBoxArrowhead:
arrowJS = fmt.Sprintf(
`node = rc.polygon(%s, { strokeWidth: %d, stroke: "%s", fill: "%s", fillStyle: "solid", seed: 1})`,
`[[0, -10], [0, 10], [-20, 10], [-20, -10]]`,
strokeWidth,
stroke,
stroke,
)
} }
return return
} }

View file

@ -462,6 +462,20 @@ a.9 <-> b.9: cf-one-required {
source-arrowhead.shape: cf-one-required source-arrowhead.shape: cf-one-required
target-arrowhead.shape: cf-one-required target-arrowhead.shape: cf-one-required
} }
a.10 <-> b.10: box {
source-arrowhead.shape: box
target-arrowhead.shape: box
}
a.11 <-> b.11: box-filled {
source-arrowhead: {
shape: box
style.filled: true
}
target-arrowhead: {
shape: box
style.filled: true
}
}
`, `,
}, },
{ {

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 132 KiB

After

Width:  |  Height:  |  Size: 146 KiB

View file

@ -289,6 +289,54 @@ func arrowheadMarker(isTarget bool, id string, connection d2target.Connection, i
} }
path = circleEl.Render() path = circleEl.Render()
case d2target.FilledBoxArrowhead:
polygonEl := d2themes.NewThemableElement("polygon", inlineTheme)
polygonEl.ClassName = "connection"
polygonEl.Fill = connection.Stroke
polygonEl.Attributes = fmt.Sprintf(`stroke-width="%d"`, connection.StrokeWidth)
if isTarget {
polygonEl.Points = fmt.Sprintf("%f,%f %f,%f %f,%f %f,%f",
0., 0.,
0., height,
width, height,
width, 0.,
)
} else {
polygonEl.Points = fmt.Sprintf("%f,%f %f,%f %f,%f %f,%f",
0., 0.,
0., height,
width, height,
width, 0.,
)
}
path = polygonEl.Render()
case d2target.BoxArrowhead:
polygonEl := d2themes.NewThemableElement("polygon", inlineTheme)
polygonEl.ClassName = "connection"
polygonEl.Fill = d2target.BG_COLOR
polygonEl.Stroke = connection.Stroke
polygonEl.Attributes = fmt.Sprintf(`stroke-width="%d"`, connection.StrokeWidth)
polygonEl.Style = fmt.Sprintf("%sstroke-linejoin:miter;", polygonEl.Style)
inset := strokeWidth / 2
if isTarget {
polygonEl.Points = fmt.Sprintf("%f,%f %f,%f %f,%f %f,%f",
inset, inset,
inset, height-inset,
width-inset, height-inset,
width-inset, inset,
)
} else {
polygonEl.Points = fmt.Sprintf("%f,%f %f,%f %f,%f %f,%f",
inset, inset,
inset, height-inset,
width-inset, height-inset,
width-inset, inset,
)
}
path = polygonEl.Render()
case d2target.CfOne, d2target.CfMany, d2target.CfOneRequired, d2target.CfManyRequired: case d2target.CfOne, d2target.CfMany, d2target.CfOneRequired, d2target.CfManyRequired:
offset := 3.0 + float64(connection.StrokeWidth)*1.8 offset := 3.0 + float64(connection.StrokeWidth)*1.8

View file

@ -755,6 +755,8 @@ const (
FilledDiamondArrowhead Arrowhead = "filled-diamond" FilledDiamondArrowhead Arrowhead = "filled-diamond"
CircleArrowhead Arrowhead = "circle" CircleArrowhead Arrowhead = "circle"
FilledCircleArrowhead Arrowhead = "filled-circle" FilledCircleArrowhead Arrowhead = "filled-circle"
BoxArrowhead Arrowhead = "box"
FilledBoxArrowhead Arrowhead = "filled-box"
// For fat arrows // For fat arrows
LineArrowhead Arrowhead = "line" LineArrowhead Arrowhead = "line"
@ -775,6 +777,7 @@ var Arrowheads = map[string]struct{}{
string(TriangleArrowhead): {}, string(TriangleArrowhead): {},
string(DiamondArrowhead): {}, string(DiamondArrowhead): {},
string(CircleArrowhead): {}, string(CircleArrowhead): {},
string(BoxArrowhead): {},
string(CfOne): {}, string(CfOne): {},
string(CfMany): {}, string(CfMany): {},
string(CfOneRequired): {}, string(CfOneRequired): {},
@ -802,6 +805,11 @@ func ToArrowhead(arrowheadType string, filled *bool) Arrowhead {
return UnfilledTriangleArrowhead return UnfilledTriangleArrowhead
} }
return TriangleArrowhead return TriangleArrowhead
case string(BoxArrowhead):
if filled != nil && *filled {
return FilledBoxArrowhead
}
return BoxArrowhead
case string(CfOne): case string(CfOne):
return CfOne return CfOne
case string(CfMany): case string(CfMany):
@ -856,6 +864,11 @@ func (arrowhead Arrowhead) Dimensions(strokeWidth float64) (width, height float6
baseHeight = 8 baseHeight = 8
widthMultiplier = 5 widthMultiplier = 5
heightMultiplier = 5 heightMultiplier = 5
case FilledBoxArrowhead, BoxArrowhead:
baseWidth = 6
baseHeight = 6
widthMultiplier = 5
heightMultiplier = 5
case CfOne, CfMany, CfOneRequired, CfManyRequired: case CfOne, CfMany, CfOneRequired, CfManyRequired:
baseWidth = 9 baseWidth = 9
baseHeight = 9 baseHeight = 9

View file

@ -2325,6 +2325,29 @@ c <-> d: filled-circle {
shape: circle shape: circle
style.filled: true style.filled: true
} }
}`,
},
{
name: "box_arrowhead",
script: `
a <-> b: box {
source-arrowhead: {
shape: box
}
target-arrowhead: {
shape: box
}
}
c <-> d: filled-box {
source-arrowhead: {
shape: box
style.filled: true
}
target-arrowhead: {
shape: box
style.filled: true
}
}`, }`,
}, },
{ {

View file

@ -215,6 +215,78 @@ filled circle: {
} }
} }
box: {
start: ""
end: ""
start.1 <-> end.1: 1 {
style.stroke-width: 1
source-arrowhead.shape: box
target-arrowhead.shape: box
}
start.2 <-> end.2: 2 {
style.stroke-width: 2
source-arrowhead.shape: box
target-arrowhead.shape: box
}
start.4 <-> end.4: 4 {
style.stroke-width: 4
source-arrowhead.shape: box
target-arrowhead.shape: box
}
start.8 <-> end.8: 8 {
style.stroke-width: 8
source-arrowhead.shape: box
target-arrowhead.shape: box
}
start.15 <-> end.15: 15 {
style.stroke-width: 15
source-arrowhead.shape: box
target-arrowhead.shape: box
}
}
filled-box: {
start: ""
end: ""
start.1 <-> end.1: 1 {
style.stroke-width: 1
source-arrowhead.shape: box
target-arrowhead.shape: box
source-arrowhead.style.filled: true
target-arrowhead.style.filled: true
}
start.2 <-> end.2: 2 {
style.stroke-width: 2
source-arrowhead.shape: box
target-arrowhead.shape: box
source-arrowhead.style.filled: true
target-arrowhead.style.filled: true
}
start.4 <-> end.4: 4 {
style.stroke-width: 4
source-arrowhead.shape: box
target-arrowhead.shape: box
source-arrowhead.style.filled: true
target-arrowhead.style.filled: true
}
start.8 <-> end.8: 8 {
style.stroke-width: 8
source-arrowhead.shape: box
target-arrowhead.shape: box
source-arrowhead.style.filled: true
target-arrowhead.style.filled: true
}
start.15 <-> end.15: 15 {
style.stroke-width: 15
source-arrowhead.shape: box
target-arrowhead.shape: box
source-arrowhead.style.filled: true
target-arrowhead.style.filled: true
}
}
cf one: { cf one: {
start: "" start: ""
end: "" end: ""

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 155 KiB

After

Width:  |  Height:  |  Size: 181 KiB

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 149 KiB

After

Width:  |  Height:  |  Size: 173 KiB

View file

@ -0,0 +1,322 @@
{
"name": "",
"config": {
"sketch": false,
"themeID": 0,
"darkThemeID": null,
"pad": null,
"center": null,
"layoutEngine": null
},
"isFolderOnly": false,
"fontFamily": "SourceSansPro",
"shapes": [
{
"id": "a",
"type": "rectangle",
"pos": {
"x": 0,
"y": 0
},
"width": 53,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "a",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 8,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "b",
"type": "rectangle",
"pos": {
"x": 0,
"y": 187
},
"width": 53,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "b",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 8,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "c",
"type": "rectangle",
"pos": {
"x": 114,
"y": 0
},
"width": 53,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "c",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 8,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "d",
"type": "rectangle",
"pos": {
"x": 113,
"y": 187
},
"width": 54,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "d",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 9,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
}
],
"connections": [
{
"id": "(a <-> b)[0]",
"src": "a",
"srcArrow": "box",
"dst": "b",
"dstArrow": "box",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "box",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 25,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"link": "",
"route": [
{
"x": 26.5,
"y": 65.5
},
{
"x": 26.5,
"y": 114.30000305175781
},
{
"x": 26.5,
"y": 138.6999969482422
},
{
"x": 26.5,
"y": 187.5
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(c <-> d)[0]",
"src": "c",
"srcArrow": "filled-box",
"dst": "d",
"dstArrow": "filled-box",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "filled-box",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 63,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"link": "",
"route": [
{
"x": 140,
"y": 65.5
},
{
"x": 140,
"y": 114.30000305175781
},
{
"x": 140,
"y": 138.6999969482422
},
{
"x": 140,
"y": 187.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": "",
"animated": false,
"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
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,304 @@
{
"name": "",
"config": {
"sketch": false,
"themeID": 0,
"darkThemeID": null,
"pad": null,
"center": null,
"layoutEngine": null
},
"isFolderOnly": false,
"fontFamily": "SourceSansPro",
"shapes": [
{
"id": "a",
"type": "rectangle",
"pos": {
"x": 12,
"y": 12
},
"width": 53,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "a",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 8,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "b",
"type": "rectangle",
"pos": {
"x": 12,
"y": 239
},
"width": 53,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "b",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 8,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "c",
"type": "rectangle",
"pos": {
"x": 85,
"y": 12
},
"width": 53,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "c",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 8,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "d",
"type": "rectangle",
"pos": {
"x": 85,
"y": 239
},
"width": 54,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"animated": false,
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "d",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 9,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
}
],
"connections": [
{
"id": "(a <-> b)[0]",
"src": "a",
"srcArrow": "box",
"dst": "b",
"dstArrow": "box",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "box",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 25,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"link": "",
"route": [
{
"x": 38.5,
"y": 78
},
{
"x": 38.5,
"y": 239
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(c <-> d)[0]",
"src": "c",
"srcArrow": "filled-box",
"dst": "d",
"dstArrow": "filled-box",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "filled-box",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 63,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"link": "",
"route": [
{
"x": 112,
"y": 78
},
{
"x": 112,
"y": 239
}
],
"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": "",
"animated": false,
"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
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 17 KiB