diff --git a/.prettierignore b/.prettierignore index d7226ae8f..6b44e5f0b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,4 +3,5 @@ d2layouts/d2elklayout/setup.js d2renderers/d2latex/mathjax.js d2renderers/d2latex/polyfills.js d2renderers/d2latex/setup.js +d2renderers/d2sketch/rough.js lib/png/generate_png.js diff --git a/README.md b/README.md index fd864cbca..bf91097cd 100644 --- a/README.md +++ b/README.md @@ -214,8 +214,10 @@ let us know and we'll be happy to include it here! - **Postgres importer**: [https://github.com/zekenie/d2-erd-from-postgres](https://github.com/zekenie/d2-erd-from-postgres) - **Structurizr to D2 exporter**: [https://github.com/goto1134/structurizr-d2-exporter](https://github.com/goto1134/structurizr-d2-exporter) - **MdBook preprocessor**: [https://github.com/danieleades/mdbook-d2](https://github.com/danieleades/mdbook-d2) +- **ROS2 D2 Exporter**: [https://github.com/Greenroom-Robotics/ros-d2](https://github.com/Greenroom-Robotics/ros-d2) - **D2 org-mode support**: [https://github.com/xcapaldi/ob-d2](https://github.com/xcapaldi/ob-d2) - **Python D2 diagram builder**: [https://github.com/MrBlenny/py-d2](https://github.com/MrBlenny/py-d2) +- **Clojure D2 transpiler**: [https://github.com/judepayne/dictim](https://github.com/judepayne/dictim) ### Misc diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index 1e0eddda8..f48533e7a 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -1,17 +1,17 @@ #### Features 🚀 -- Tooltips can be set on shapes. See [https://d2lang.com/tour/tooltips](https://d2lang.com/tour/interactive). [#548](https://github.com/terrastruct/d2/pull/548) -- Links can be set on shapes. See [https://d2lang.com/tour/tooltips](https://d2lang.com/tour/interactive). [#548](https://github.com/terrastruct/d2/pull/548) -- The `width` and `height` attributes are no longer restricted to images and can be applied to non-container shapes. [#498](https://github.com/terrastruct/d2/pull/498) +- Crow foot notation is now supported. [#578](https://github.com/terrastruct/d2/pull/578) +- Exported SVGs also fit to screen on open. [#601](https://github.com/terrastruct/d2/pull/601) #### Improvements 🧹 -- Watch mode now renders fit to screen. [#560](https://github.com/terrastruct/d2/pull/560) - #### Bugfixes ⛑️ - Restricts where `near` key constant values can be used, with good error messages, instead of erroring (e.g. setting `near: top-center` on a container would cause bad layouts or error). [#538](https://github.com/terrastruct/d2/pull/538) - Fixes an error during ELK layout when images had empty labels. [#555](https://github.com/terrastruct/d2/pull/555) - Fixes rendering classes and tables with empty headers. [#498](https://github.com/terrastruct/d2/pull/498) - Fixes rendering sql tables with no columns. [#553](https://github.com/terrastruct/d2/pull/553) -- Fixes routing between sql table columns if the column name is the prefix of the table name [#615](https://github.com/terrastruct/d2/pull/615) \ No newline at end of file +- Appendix seperator line no longer added to PNG export when appendix doesn't exist. [#582](https://github.com/terrastruct/d2/pull/582) +- Watch mode only fits to screen on initial load. [#601](https://github.com/terrastruct/d2/pull/601) +- Dimensions (`width`/`height`) were incorrectly giving compiler errors when applied on a shape with style. [#614](https://github.com/terrastruct/d2/pull/614) +- Fixes routing between sql table columns if the column name is the prefix of the table name [#615](https://github.com/terrastruct/d2/pull/615) diff --git a/ci/release/changelogs/v0.1.4.md b/ci/release/changelogs/v0.1.4.md new file mode 100644 index 000000000..07a1668db --- /dev/null +++ b/ci/release/changelogs/v0.1.4.md @@ -0,0 +1,35 @@ +This release marks the introduction of interactive diagrams. Namely, `tooltip` and `link` can now be set, which allows you to hover to see more or click to go to an external link. This small change enables many possibilities, including richer integrations like internal wiki's that can be linked together through diagrams. An icon will indicate that a shape has a tooltip that can be hovered over for more information, or a link. + +Screen Shot 2022-12-30 at 6 47 45 PM + +Since interactive features obviously won't work on static export formats like PNG, they will be included automatically in an appendix when exporting to those formats, like so: + +![tooltip](https://user-images.githubusercontent.com/3120367/210122793-582d3fc7-8e09-46f1-bb78-5dcc6cf1de55.png) + +This release also gives more power to configure layouts. `width` and `height` are D2 keywords which previouslly only worked on images, but now work on any non-containers. Additionally, all the layout engines have configurations exposed. D2 sets sensible defaults to each layout engine without any input, so this is meant to be an advanced feature for users who want that extra control. + +Happy new years! + +#### Features 🚀 + +- Tooltips can be set on shapes. See [https://d2lang.com/tour/interactive](https://d2lang.com/tour/interactive). [#548](https://github.com/terrastruct/d2/pull/548) +- Links can be set on shapes. See [https://d2lang.com/tour/interactive](https://d2lang.com/tour/interactive). [#548](https://github.com/terrastruct/d2/pull/548) +- The `width` and `height` attributes are no longer restricted to images and can be applied to non-container shapes. [#498](https://github.com/terrastruct/d2/pull/498) +- Layout engine options are exposed and configurable. See individual layout pages on [https://d2lang.com/tour/layouts](https://d2lang.com/tour/layouts) for list of configurations. [#563](https://github.com/terrastruct/d2/pull/563) + +#### Improvements 🧹 + +- Watch mode renders fit to screen. [#560](https://github.com/terrastruct/d2/pull/560) + +#### Bugfixes ⛑️ + +- Fixes rendering `class` and `table` with empty headers. [#498](https://github.com/terrastruct/d2/pull/498) +- Fixes rendering of `sql_table` with no columns. [#553](https://github.com/terrastruct/d2/pull/553) +- Diagram bounding boxes account for stroke widths. [#574](https://github.com/terrastruct/d2/pull/574) +- Restricts where `near` key constant values can be used, with good error messages, instead of erroring (e.g. setting `near: top-center` on a container would cause bad layouts or error). [#538](https://github.com/terrastruct/d2/pull/538) +- Fixes panic when images with empty labels are rendered with ELK. [#555](https://github.com/terrastruct/d2/pull/555) + +#### Breaking changes + +- For usages of D2 as a library, `d2dagrelayout.Layout` and `d2elklayout.Layout` now accept a third parameter for options. If you would like to keep the defaults, please change your code to call `dagrelayout.DefaultLayout` and `d2elklayout.DefaultLayout` respectively. + diff --git a/ci/release/template/man/d2.1 b/ci/release/template/man/d2.1 index a484ebe8f..b1bcb709c 100644 --- a/ci/release/template/man/d2.1 +++ b/ci/release/template/man/d2.1 @@ -82,7 +82,7 @@ Print version information and exit. .It Ar layout Lists available layout engine options with short help. .It Ar layout Op Ar name -Display long help for a particular layout engine. +Display long help for a particular layout engine, including its configuration options. .It Ar fmt Ar file.d2 Format .Ar file.d2 diff --git a/ci/sub b/ci/sub index b1ec0a8d4..9a29d9ea6 160000 --- a/ci/sub +++ b/ci/sub @@ -1 +1 @@ -Subproject commit b1ec0a8d430a62b7556211ed8bcd7b6e41e2362c +Subproject commit 9a29d9ea640834905c4010c0b3d14b7301ebb6d8 diff --git a/cmd/d2plugin-dagre/main.go b/cmd/d2plugin-dagre/main.go index 5dbed2235..96130300e 100644 --- a/cmd/d2plugin-dagre/main.go +++ b/cmd/d2plugin-dagre/main.go @@ -9,5 +9,5 @@ import ( ) func main() { - xmain.Main(d2plugin.Serve(d2plugin.DagrePlugin)) + xmain.Main(d2plugin.Serve(&d2plugin.DagrePlugin)) } diff --git a/d2chaos/d2chaos_test.go b/d2chaos/d2chaos_test.go index d5ac6a17d..8b73e4e82 100644 --- a/d2chaos/d2chaos_test.go +++ b/d2chaos/d2chaos_test.go @@ -123,7 +123,7 @@ func test(t *testing.T, textPath, text string) { err = g.SetDimensions(nil, ruler, nil) assert.Nil(t, err) - err = d2dagrelayout.Layout(ctx, g) + err = d2dagrelayout.DefaultLayout(ctx, g) if err != nil { t.Fatal(err) } diff --git a/d2compiler/compile.go b/d2compiler/compile.go index 51014adc0..21e4c1cc4 100644 --- a/d2compiler/compile.go +++ b/d2compiler/compile.go @@ -836,7 +836,7 @@ func (c *compiler) validateKey(obj *d2graph.Object, m *d2ast.Map, mk *d2ast.Key) switch strings.ToLower(obj.Attributes.Shape.Value) { case d2target.ShapeSQLTable, d2target.ShapeClass: default: - if len(obj.Children) > 0 && (reserved == "width" || reserved == "height") { + if len(obj.Children) > 0 && !(len(obj.Children) == 1 && obj.ChildrenArray[0].ID == "style") && (reserved == "width" || reserved == "height") { c.errorf(mk.Range.Start, mk.Range.End, fmt.Sprintf("%s cannot be used on container: %s", reserved, obj.AbsID())) } } diff --git a/d2compiler/compile_test.go b/d2compiler/compile_test.go index ddb51cf8c..81db413f2 100644 --- a/d2compiler/compile_test.go +++ b/d2compiler/compile_test.go @@ -208,6 +208,15 @@ d2/testdata/d2compiler/TestCompile/no_dimensions_on_containers.d2:25:3: width ca d2/testdata/d2compiler/TestCompile/no_dimensions_on_containers.d2:26:3: height cannot be used on container: containers.oval container d2/testdata/d2compiler/TestCompile/no_dimensions_on_containers.d2:36:3: width cannot be used on container: containers.hexagon container d2/testdata/d2compiler/TestCompile/no_dimensions_on_containers.d2:37:3: height cannot be used on container: containers.hexagon container +`, + }, + { + name: "dimension_with_style", + + text: `x: { + width: 200 + style.multiple: true +} `, }, { diff --git a/d2exporter/export_test.go b/d2exporter/export_test.go index ca813d2b1..613090be5 100644 --- a/d2exporter/export_test.go +++ b/d2exporter/export_test.go @@ -239,7 +239,7 @@ func run(t *testing.T, tc testCase) { err = g.SetDimensions(nil, ruler, nil) assert.JSON(t, nil, err) - err = d2sequence.Layout(ctx, g, d2dagrelayout.Layout) + err = d2sequence.Layout(ctx, g, d2dagrelayout.DefaultLayout) if err != nil { t.Fatal(err) } diff --git a/d2graph/d2graph.go b/d2graph/d2graph.go index 7bd59ec6a..21361304a 100644 --- a/d2graph/d2graph.go +++ b/d2graph/d2graph.go @@ -1067,6 +1067,22 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler if obj.Attributes.Height != nil { desiredHeight, _ = strconv.Atoi(obj.Attributes.Height.Value) } + + if obj.Attributes.Label.Value == "" && + obj.Attributes.Shape.Value != d2target.ShapeImage && + obj.Attributes.Shape.Value != d2target.ShapeSQLTable && + obj.Attributes.Shape.Value != d2target.ShapeClass { + obj.Width = DEFAULT_SHAPE_PADDING + obj.Height = DEFAULT_SHAPE_PADDING + if desiredWidth != 0 { + obj.Width = float64(desiredWidth) + } + if desiredHeight != 0 { + obj.Height = float64(desiredHeight) + } + continue + } + shapeType := strings.ToLower(obj.Attributes.Shape.Value) labelDims, err := obj.GetLabelSize(mtexts, ruler, fontFamily) diff --git a/d2layouts/d2dagrelayout/layout.go b/d2layouts/d2dagrelayout/layout.go index 68d8ec0f6..9969d2080 100644 --- a/d2layouts/d2dagrelayout/layout.go +++ b/d2layouts/d2dagrelayout/layout.go @@ -30,6 +30,16 @@ var setupJS string //go:embed dagre.js var dagreJS string +type ConfigurableOpts struct { + NodeSep int `json:"nodesep"` + EdgeSep int `json:"edgesep"` +} + +var DefaultOpts = ConfigurableOpts{ + NodeSep: 60, + EdgeSep: 40, +} + type DagreNode struct { ID string `json:"id"` X float64 `json:"x"` @@ -42,16 +52,23 @@ type DagreEdge struct { Points []*geo.Point `json:"points"` } -type dagreGraphAttrs struct { +type dagreOpts struct { // for a top to bottom graph: ranksep is y spacing, nodesep is x spacing, edgesep is x spacing ranksep int - edgesep int - nodesep int // graph direction: tb (top to bottom)| bt | lr | rl rankdir string + + ConfigurableOpts } -func Layout(ctx context.Context, g *d2graph.Graph) (err error) { +func DefaultLayout(ctx context.Context, g *d2graph.Graph) (err error) { + return Layout(ctx, g, nil) +} + +func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err error) { + if opts == nil { + opts = &DefaultOpts + } defer xdefer.Errorf(&err, "failed to dagre layout") debugJS := false @@ -63,9 +80,11 @@ func Layout(ctx context.Context, g *d2graph.Graph) (err error) { return err } - rootAttrs := dagreGraphAttrs{ - edgesep: 40, - nodesep: 60, + rootAttrs := dagreOpts{ + ConfigurableOpts: ConfigurableOpts{ + EdgeSep: opts.EdgeSep, + NodeSep: opts.NodeSep, + }, } isHorizontal := false switch g.Root.Attributes.Direction.Value { @@ -266,7 +285,7 @@ func Layout(ctx context.Context, g *d2graph.Graph) (err error) { return nil } -func setGraphAttrs(attrs dagreGraphAttrs) string { +func setGraphAttrs(attrs dagreOpts) string { return fmt.Sprintf(`g.setGraph({ ranksep: %d, edgesep: %d, @@ -275,8 +294,8 @@ func setGraphAttrs(attrs dagreGraphAttrs) string { }); `, attrs.ranksep, - attrs.edgesep, - attrs.nodesep, + attrs.ConfigurableOpts.EdgeSep, + attrs.ConfigurableOpts.NodeSep, attrs.rankdir, ) } diff --git a/d2layouts/d2elklayout/layout.go b/d2layouts/d2elklayout/layout.go index ada0ca0e7..d9b333385 100644 --- a/d2layouts/d2elklayout/layout.go +++ b/d2layouts/d2elklayout/layout.go @@ -31,23 +31,23 @@ var elkJS string var setupJS string type ELKNode struct { - ID string `json:"id"` - X float64 `json:"x"` - Y float64 `json:"y"` - Width float64 `json:"width"` - Height float64 `json:"height"` - Children []*ELKNode `json:"children,omitempty"` - Labels []*ELKLabel `json:"labels,omitempty"` - LayoutOptions *ELKLayoutOptions `json:"layoutOptions,omitempty"` + ID string `json:"id"` + X float64 `json:"x"` + Y float64 `json:"y"` + Width float64 `json:"width"` + Height float64 `json:"height"` + Children []*ELKNode `json:"children,omitempty"` + Labels []*ELKLabel `json:"labels,omitempty"` + LayoutOptions *elkOpts `json:"layoutOptions,omitempty"` } type ELKLabel struct { - Text string `json:"text"` - X float64 `json:"x"` - Y float64 `json:"y"` - Width float64 `json:"width"` - Height float64 `json:"height"` - LayoutOptions *ELKLayoutOptions `json:"layoutOptions,omitempty"` + Text string `json:"text"` + X float64 `json:"x"` + Y float64 `json:"y"` + Width float64 `json:"width"` + Height float64 `json:"height"` + LayoutOptions *elkOpts `json:"layoutOptions,omitempty"` } type ELKPoint struct { @@ -71,26 +71,46 @@ type ELKEdge struct { } type ELKGraph struct { - ID string `json:"id"` - LayoutOptions *ELKLayoutOptions `json:"layoutOptions"` - Children []*ELKNode `json:"children,omitempty"` - Edges []*ELKEdge `json:"edges,omitempty"` + ID string `json:"id"` + LayoutOptions *elkOpts `json:"layoutOptions"` + Children []*ELKNode `json:"children,omitempty"` + Edges []*ELKEdge `json:"edges,omitempty"` } -type ELKLayoutOptions struct { - Algorithm string `json:"elk.algorithm,omitempty"` - HierarchyHandling string `json:"elk.hierarchyHandling,omitempty"` - NodeSpacing float64 `json:"spacing.nodeNodeBetweenLayers,omitempty"` - Padding string `json:"elk.padding,omitempty"` - EdgeNodeSpacing float64 `json:"spacing.edgeNodeBetweenLayers,omitempty"` - Direction string `json:"elk.direction"` - SelfLoopSpacing float64 `json:"elk.spacing.nodeSelfLoop"` - InlineEdgeLabels bool `json:"elk.edgeLabels.inline,omitempty"` - ConsiderModelOrder string `json:"elk.layered.considerModelOrder.strategy,omitempty"` - ForceNodeModelOrder bool `json:"elk.layered.crossingMinimization.forceNodeModelOrder,omitempty"` +type ConfigurableOpts struct { + Algorithm string `json:"elk.algorithm,omitempty"` + NodeSpacing int `json:"spacing.nodeNodeBetweenLayers,omitempty"` + Padding string `json:"elk.padding,omitempty"` + EdgeNodeSpacing int `json:"spacing.edgeNodeBetweenLayers,omitempty"` + SelfLoopSpacing int `json:"elk.spacing.nodeSelfLoop"` } -func Layout(ctx context.Context, g *d2graph.Graph) (err error) { +var DefaultOpts = ConfigurableOpts{ + Algorithm: "layered", + NodeSpacing: 100.0, + Padding: "[top=75,left=75,bottom=75,right=75]", + EdgeNodeSpacing: 50.0, + SelfLoopSpacing: 50.0, +} + +type elkOpts struct { + Direction string `json:"elk.direction"` + HierarchyHandling string `json:"elk.hierarchyHandling,omitempty"` + InlineEdgeLabels bool `json:"elk.edgeLabels.inline,omitempty"` + ForceNodeModelOrder bool `json:"elk.layered.crossingMinimization.forceNodeModelOrder,omitempty"` + ConsiderModelOrder string `json:"elk.layered.considerModelOrder.strategy,omitempty"` + + ConfigurableOpts +} + +func DefaultLayout(ctx context.Context, g *d2graph.Graph) (err error) { + return Layout(ctx, g, nil) +} + +func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err error) { + if opts == nil { + opts = &DefaultOpts + } defer xdefer.Errorf(&err, "failed to ELK layout") vm := goja.New() @@ -109,13 +129,15 @@ func Layout(ctx context.Context, g *d2graph.Graph) (err error) { elkGraph := &ELKGraph{ ID: "root", - LayoutOptions: &ELKLayoutOptions{ - Algorithm: "layered", + LayoutOptions: &elkOpts{ HierarchyHandling: "INCLUDE_CHILDREN", - NodeSpacing: 100.0, - EdgeNodeSpacing: 50.0, - SelfLoopSpacing: 50.0, ConsiderModelOrder: "NODES_AND_EDGES", + ConfigurableOpts: ConfigurableOpts{ + Algorithm: opts.Algorithm, + NodeSpacing: opts.NodeSpacing, + EdgeNodeSpacing: opts.EdgeNodeSpacing, + SelfLoopSpacing: opts.SelfLoopSpacing, + }, }, } switch g.Root.Attributes.Direction.Value { @@ -160,9 +182,11 @@ func Layout(ctx context.Context, g *d2graph.Graph) (err error) { } if len(obj.ChildrenArray) > 0 { - n.LayoutOptions = &ELKLayoutOptions{ - Padding: "[top=75,left=75,bottom=75,right=75]", + n.LayoutOptions = &elkOpts{ ForceNodeModelOrder: true, + ConfigurableOpts: ConfigurableOpts{ + Padding: opts.Padding, + }, } } @@ -193,7 +217,7 @@ func Layout(ctx context.Context, g *d2graph.Graph) (err error) { Text: edge.Attributes.Label.Value, Width: float64(edge.LabelDimensions.Width), Height: float64(edge.LabelDimensions.Height), - LayoutOptions: &ELKLayoutOptions{ + LayoutOptions: &elkOpts{ InlineEdgeLabels: true, }, }) diff --git a/d2lib/c.go b/d2lib/c.go deleted file mode 100644 index c62ef79ae..000000000 --- a/d2lib/c.go +++ /dev/null @@ -1,7 +0,0 @@ -package d2lib - -import "oss.terrastruct.com/d2/d2layouts/d2dagrelayout" - -func init() { - dagreLayout = d2dagrelayout.Layout -} diff --git a/d2lib/d2.go b/d2lib/d2.go index 936124b9f..bfe9713a9 100644 --- a/d2lib/d2.go +++ b/d2lib/d2.go @@ -9,6 +9,7 @@ import ( "oss.terrastruct.com/d2/d2compiler" "oss.terrastruct.com/d2/d2exporter" "oss.terrastruct.com/d2/d2graph" + "oss.terrastruct.com/d2/d2layouts/d2dagrelayout" "oss.terrastruct.com/d2/d2layouts/d2near" "oss.terrastruct.com/d2/d2layouts/d2sequence" "oss.terrastruct.com/d2/d2renderers/d2fonts" @@ -74,12 +75,12 @@ func Compile(ctx context.Context, input string, opts *CompileOptions) (*d2target func getLayout(opts *CompileOptions) (func(context.Context, *d2graph.Graph) error, error) { if opts.Layout != nil { return opts.Layout, nil - } else if os.Getenv("D2_LAYOUT") == "dagre" && dagreLayout != nil { - return dagreLayout, nil + } else if os.Getenv("D2_LAYOUT") == "dagre" { + defaultLayout := func(ctx context.Context, g *d2graph.Graph) error { + return d2dagrelayout.Layout(ctx, g, nil) + } + return defaultLayout, nil } else { return nil, errors.New("no available layout") } } - -// See c.go -var dagreLayout func(context.Context, *d2graph.Graph) error diff --git a/d2plugin/exec.go b/d2plugin/exec.go index b4929dcc2..b76a802d0 100644 --- a/d2plugin/exec.go +++ b/d2plugin/exec.go @@ -7,9 +7,11 @@ import ( "errors" "fmt" "os/exec" + "strconv" "time" "oss.terrastruct.com/util-go/xdefer" + "oss.terrastruct.com/util-go/xmain" "oss.terrastruct.com/d2/d2graph" ) @@ -37,9 +39,65 @@ import ( // the error to stderr. type execPlugin struct { path string + opts map[string]string + info *PluginInfo } -func (p execPlugin) Info(ctx context.Context) (_ *PluginInfo, err error) { +func (p *execPlugin) Flags(ctx context.Context) (_ []PluginSpecificFlag, err error) { + ctx, cancel := context.WithTimeout(ctx, time.Second*10) + defer cancel() + cmd := exec.CommandContext(ctx, p.path, "flags") + defer xdefer.Errorf(&err, "failed to run %v", cmd.Args) + + stdout, err := cmd.Output() + if err != nil { + ee := &exec.ExitError{} + if errors.As(err, &ee) && len(ee.Stderr) > 0 { + return nil, fmt.Errorf("%v\nstderr:\n%s", ee, ee.Stderr) + } + return nil, err + } + + var flags []PluginSpecificFlag + + err = json.Unmarshal(stdout, &flags) + if err != nil { + return nil, fmt.Errorf("failed to unmarshal json: %w", err) + } + + return flags, nil +} + +func (p *execPlugin) HydrateOpts(opts []byte) error { + if opts != nil { + var execOpts map[string]interface{} + err := json.Unmarshal(opts, &execOpts) + if err != nil { + return xmain.UsageErrorf("non-exec layout options given for exec") + } + + allString := make(map[string]string) + for k, v := range execOpts { + switch vt := v.(type) { + case string: + allString[k] = vt + case int64: + allString[k] = strconv.Itoa(int(vt)) + case float64: + allString[k] = strconv.Itoa(int(vt)) + } + } + + p.opts = allString + } + return nil +} + +func (p *execPlugin) Info(ctx context.Context) (_ *PluginInfo, err error) { + if p.info != nil { + return p.info, nil + } + ctx, cancel := context.WithTimeout(ctx, time.Second*10) defer cancel() cmd := exec.CommandContext(ctx, p.path, "info") @@ -61,10 +119,11 @@ func (p execPlugin) Info(ctx context.Context) (_ *PluginInfo, err error) { return nil, fmt.Errorf("failed to unmarshal json: %w", err) } + p.info = &info return &info, nil } -func (p execPlugin) Layout(ctx context.Context, g *d2graph.Graph) error { +func (p *execPlugin) Layout(ctx context.Context, g *d2graph.Graph) error { ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() @@ -73,7 +132,11 @@ func (p execPlugin) Layout(ctx context.Context, g *d2graph.Graph) error { return err } - cmd := exec.CommandContext(ctx, p.path, "layout") + args := []string{"layout"} + for k, v := range p.opts { + args = append(args, fmt.Sprintf("--%s", k), v) + } + cmd := exec.CommandContext(ctx, p.path, args...) buffer := bytes.Buffer{} buffer.Write(graphBytes) @@ -95,7 +158,7 @@ func (p execPlugin) Layout(ctx context.Context, g *d2graph.Graph) error { return nil } -func (p execPlugin) PostProcess(ctx context.Context, in []byte) ([]byte, error) { +func (p *execPlugin) PostProcess(ctx context.Context, in []byte) ([]byte, error) { ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() diff --git a/d2plugin/plugin.go b/d2plugin/plugin.go index 56798c88f..a6eefdf7e 100644 --- a/d2plugin/plugin.go +++ b/d2plugin/plugin.go @@ -7,9 +7,11 @@ package d2plugin import ( "context" + "encoding/json" "os/exec" "oss.terrastruct.com/util-go/xexec" + "oss.terrastruct.com/util-go/xmain" "oss.terrastruct.com/d2/d2graph" ) @@ -19,10 +21,40 @@ import ( // See plugin_* files for the plugins available for bundling. var plugins []Plugin +type PluginSpecificFlag struct { + Name string + Type string + Default interface{} + Usage string + // Must match the tag in the opt + Tag string +} + +func (f *PluginSpecificFlag) AddToOpts(opts *xmain.Opts) { + switch f.Type { + case "string": + opts.String("", f.Name, "", f.Default.(string), f.Usage) + case "int64": + var val int64 + switch defaultType := f.Default.(type) { + case int64: + val = defaultType + case float64: + // json unmarshals numbers to float64 + val = int64(defaultType) + } + opts.Int64("", f.Name, "", val, f.Usage) + } +} + type Plugin interface { // Info returns the current info information of the plugin. Info(context.Context) (*PluginInfo, error) + Flags(context.Context) ([]PluginSpecificFlag, error) + + HydrateOpts([]byte) error + // Layout runs the plugin's autolayout algorithm on the input graph // and returns a new graph with the computed placements. Layout(context.Context, *d2graph.Graph) error @@ -48,36 +80,54 @@ type PluginInfo struct { const binaryPrefix = "d2plugin-" -func ListPlugins(ctx context.Context) ([]*PluginInfo, error) { +func ListPlugins(ctx context.Context) ([]Plugin, error) { // 1. Run Info on all bundled plugins in the global plugins array. // - set Type for each bundled plugin to "bundled". // 2. Iterate through directories in $PATH and look for executables within these // directories with the prefix d2plugin-* // 3. Run each plugin binary with the argument info. e.g. d2plugin-dagre info - var infoSlice []*PluginInfo - - for _, p := range plugins { - info, err := p.Info(ctx) - if err != nil { - return nil, err - } - info.Type = "bundled" - infoSlice = append(infoSlice, info) - } + var ps []Plugin + ps = append(ps, plugins...) matches, err := xexec.SearchPath(binaryPrefix) if err != nil { return nil, err } +BINARY_PLUGINS_LOOP: for _, path := range matches { p := &execPlugin{path: path} info, err := p.Info(ctx) if err != nil { return nil, err } - info.Type = "binary" - info.Path = path + for _, p2 := range ps { + info2, err := p2.Info(ctx) + if err != nil { + return nil, err + } + if info.Name == info2.Name { + continue BINARY_PLUGINS_LOOP + } + } + ps = append(ps, p) + } + return ps, nil +} + +func ListPluginInfos(ctx context.Context, ps []Plugin) ([]*PluginInfo, error) { + var infoSlice []*PluginInfo + for _, p := range ps { + info, err := p.Info(ctx) + if err != nil { + return nil, err + } + if ep, ok := p.(*execPlugin); ok { + info.Type = "binary" + info.Path = ep.path + } else { + info.Type = "bundled" + } infoSlice = append(infoSlice, info) } @@ -91,21 +141,53 @@ func ListPlugins(ctx context.Context) ([]*PluginInfo, error) { // **NOTE** When D2 upgrades to go 1.19, remember to ignore exec.ErrDot // 3. If such a binary is found, it builds an execPlugin in exec.go // to get a plugin implementation around the binary and returns it. -func FindPlugin(ctx context.Context, name string) (Plugin, string, error) { - for _, p := range plugins { +func FindPlugin(ctx context.Context, ps []Plugin, name string) (Plugin, error) { + for _, p := range ps { info, err := p.Info(ctx) if err != nil { - return nil, "", err + return nil, err } if info.Name == name { - return p, "", nil + return p, nil + } + } + return nil, exec.ErrNotFound +} + +func ListPluginFlags(ctx context.Context, ps []Plugin) ([]PluginSpecificFlag, error) { + var out []PluginSpecificFlag + for _, p := range ps { + flags, err := p.Flags(ctx) + if err != nil { + return nil, err + } + out = append(out, flags...) + } + + return out, nil +} + +func HydratePluginOpts(ctx context.Context, ms *xmain.State, plugin Plugin) error { + opts := make(map[string]interface{}) + flags, err := plugin.Flags(ctx) + if err != nil { + return err + } + for _, f := range flags { + switch f.Type { + case "string": + val, _ := ms.Opts.Flags.GetString(f.Name) + opts[f.Tag] = val + case "int64": + val, _ := ms.Opts.Flags.GetInt64(f.Name) + opts[f.Tag] = val } } - path, err := exec.LookPath(binaryPrefix + name) + b, err := json.Marshal(opts) if err != nil { - return nil, "", err + return err } - return &execPlugin{path: path}, path, nil + return plugin.HydrateOpts(b) } diff --git a/d2plugin/plugin_dagre.go b/d2plugin/plugin_dagre.go index 228011ac0..c3f16a8f1 100644 --- a/d2plugin/plugin_dagre.go +++ b/d2plugin/plugin_dagre.go @@ -4,36 +4,82 @@ package d2plugin import ( "context" + "encoding/json" + "fmt" "oss.terrastruct.com/d2/d2graph" "oss.terrastruct.com/d2/d2layouts/d2dagrelayout" + "oss.terrastruct.com/util-go/xmain" ) var DagrePlugin = dagrePlugin{} func init() { - plugins = append(plugins, DagrePlugin) + plugins = append(plugins, &DagrePlugin) } -type dagrePlugin struct{} +type dagrePlugin struct { + opts *d2dagrelayout.ConfigurableOpts +} + +func (p dagrePlugin) Flags(context.Context) ([]PluginSpecificFlag, error) { + return []PluginSpecificFlag{ + { + Name: "dagre-nodesep", + Type: "int64", + Default: int64(d2dagrelayout.DefaultOpts.NodeSep), + Usage: "number of pixels that separate nodes horizontally.", + Tag: "nodesep", + }, + { + Name: "dagre-edgesep", + Type: "int64", + Default: int64(d2dagrelayout.DefaultOpts.EdgeSep), + Usage: "number of pixels that separate edges horizontally.", + Tag: "edgesep", + }, + }, nil +} + +func (p *dagrePlugin) HydrateOpts(opts []byte) error { + if opts != nil { + var dagreOpts d2dagrelayout.ConfigurableOpts + err := json.Unmarshal(opts, &dagreOpts) + if err != nil { + return xmain.UsageErrorf("non-dagre layout options given for dagre") + } + + p.opts = &dagreOpts + } + return nil +} + +func (p dagrePlugin) Info(ctx context.Context) (*PluginInfo, error) { + opts := xmain.NewOpts(nil, nil, nil) + flags, err := p.Flags(ctx) + if err != nil { + return nil, err + } + for _, f := range flags { + f.AddToOpts(opts) + } -func (p dagrePlugin) Info(context.Context) (*PluginInfo, error) { return &PluginInfo{ Name: "dagre", ShortHelp: "The directed graph layout library Dagre", - LongHelp: `dagre is a directed graph layout library for JavaScript. + LongHelp: fmt.Sprintf(`dagre is a directed graph layout library for JavaScript. See https://github.com/dagrejs/dagre -The implementation of this plugin is at: https://github.com/terrastruct/d2/tree/master/d2plugin/d2dagrelayout -note: dagre is the primary layout algorithm for text to diagram generator Mermaid.js. - See https://github.com/mermaid-js/mermaid - We have a useful comparison at https://text-to-diagram.com/?example=basic&a=d2&b=mermaid -`, +Flags correspond to ones found at https://github.com/dagrejs/dagre/wiki. See dagre's reference for more on each. + +Flags: +%s +`, opts.Defaults()), }, nil } func (p dagrePlugin) Layout(ctx context.Context, g *d2graph.Graph) error { - return d2dagrelayout.Layout(ctx, g) + return d2dagrelayout.Layout(ctx, g, p.opts) } func (p dagrePlugin) PostProcess(ctx context.Context, in []byte) ([]byte, error) { diff --git a/d2plugin/plugin_elk.go b/d2plugin/plugin_elk.go index 6a8567f5e..631e953ba 100644 --- a/d2plugin/plugin_elk.go +++ b/d2plugin/plugin_elk.go @@ -4,31 +4,103 @@ package d2plugin import ( "context" + "encoding/json" + "fmt" "oss.terrastruct.com/d2/d2graph" "oss.terrastruct.com/d2/d2layouts/d2elklayout" + "oss.terrastruct.com/util-go/xmain" ) var ELKPlugin = elkPlugin{} func init() { - plugins = append(plugins, ELKPlugin) + plugins = append(plugins, &ELKPlugin) } -type elkPlugin struct{} +type elkPlugin struct { + opts *d2elklayout.ConfigurableOpts +} -func (p elkPlugin) Info(context.Context) (*PluginInfo, error) { +func (p elkPlugin) Flags(context.Context) ([]PluginSpecificFlag, error) { + return []PluginSpecificFlag{ + { + Name: "elk-algorithm", + Type: "string", + Default: d2elklayout.DefaultOpts.Algorithm, + Usage: "layout algorithm", + Tag: "elk.algorithm", + }, + { + Name: "elk-nodeNodeBetweenLayers", + Type: "int64", + Default: int64(d2elklayout.DefaultOpts.NodeSpacing), + Usage: "the spacing to be preserved between any pair of nodes of two adjacent layers", + Tag: "spacing.nodeNodeBetweenLayers", + }, + { + Name: "elk-padding", + Type: "string", + Default: d2elklayout.DefaultOpts.Padding, + Usage: "the padding to be left to a parent element’s border when placing child elements", + Tag: "elk.padding", + }, + { + Name: "elk-edgeNodeBetweenLayers", + Type: "int64", + Default: int64(d2elklayout.DefaultOpts.EdgeNodeSpacing), + Usage: "the spacing to be preserved between nodes and edges that are routed next to the node’s layer", + Tag: "spacing.edgeNodeBetweenLayers", + }, + { + Name: "elk-nodeSelfLoop", + Type: "int64", + Default: int64(d2elklayout.DefaultOpts.SelfLoopSpacing), + Usage: "spacing to be preserved between a node and its self loops", + Tag: "elk.spacing.nodeSelfLoop", + }, + }, nil +} + +func (p *elkPlugin) HydrateOpts(opts []byte) error { + if opts != nil { + var elkOpts d2elklayout.ConfigurableOpts + err := json.Unmarshal(opts, &elkOpts) + if err != nil { + return xmain.UsageErrorf("non-ELK layout options given for ELK") + } + + p.opts = &elkOpts + } + return nil +} + +func (p elkPlugin) Info(ctx context.Context) (*PluginInfo, error) { + opts := xmain.NewOpts(nil, nil, nil) + flags, err := p.Flags(ctx) + if err != nil { + return nil, err + } + for _, f := range flags { + f.AddToOpts(opts) + } return &PluginInfo{ Name: "elk", ShortHelp: "Eclipse Layout Kernel (ELK) with the Layered algorithm.", - LongHelp: `ELK is a layout engine offered by Eclipse. + LongHelp: fmt.Sprintf(`ELK is a layout engine offered by Eclipse. Originally written in Java, it has been ported to Javascript and cross-compiled into D2. -See https://github.com/kieler/elkjs for more.`, +See https://github.com/kieler/elkjs for more. + +Flags correspond to ones found at https://www.eclipse.org/elk/reference.html. See ELK's reference for more on each. + +Flags: +%s +`, opts.Defaults()), }, nil } func (p elkPlugin) Layout(ctx context.Context, g *d2graph.Graph) error { - return d2elklayout.Layout(ctx, g) + return d2elklayout.Layout(ctx, g, p.opts) } func (p elkPlugin) PostProcess(ctx context.Context, in []byte) ([]byte, error) { diff --git a/d2plugin/serve.go b/d2plugin/serve.go index b13680746..acc697a96 100644 --- a/d2plugin/serve.go +++ b/d2plugin/serve.go @@ -21,6 +21,13 @@ import ( // Also see execPlugin in exec.go for the d2 binary plugin protocol. func Serve(p Plugin) xmain.RunFunc { return func(ctx context.Context, ms *xmain.State) (err error) { + fs, err := p.Flags(ctx) + if err != nil { + return err + } + for _, f := range fs { + f.AddToOpts(ms.Opts) + } err = ms.Opts.Flags.Parse(ms.Opts.Args) if !errors.Is(err, pflag.ErrHelp) && err != nil { return xmain.UsageErrorf("failed to parse flags: %v", err) @@ -34,10 +41,17 @@ func Serve(p Plugin) xmain.RunFunc { return xmain.UsageErrorf("expected first argument to be subcmd name") } + err = HydratePluginOpts(ctx, ms, p) + if err != nil { + return err + } + subcmd := ms.Opts.Flags.Arg(0) switch subcmd { case "info": return info(ctx, p, ms) + case "flags": + return flags(ctx, p, ms) case "layout": return layout(ctx, p, ms) case "postprocess": @@ -64,6 +78,22 @@ func info(ctx context.Context, p Plugin, ms *xmain.State) error { return nil } +func flags(ctx context.Context, p Plugin, ms *xmain.State) error { + flags, err := p.Flags(ctx) + if err != nil { + return err + } + b, err := json.Marshal(flags) + if err != nil { + return err + } + _, err = ms.Stdout.Write(b) + if err != nil { + return err + } + return nil +} + func layout(ctx context.Context, p Plugin, ms *xmain.State) error { in, err := io.ReadAll(ms.Stdin) if err != nil { diff --git a/d2renderers/d2sketch/rough.js b/d2renderers/d2sketch/rough.js index 04eabe71a..b19e8c7d7 100644 --- a/d2renderers/d2sketch/rough.js +++ b/d2renderers/d2sketch/rough.js @@ -1261,7 +1261,7 @@ class B { n = []; if (!t) return this._drawable("path", n, s); const i = (function (t, e) { - t = (t || "").replace(/\n/g, " ").replace(/(-\s)/g, "-").replace("/(ss)/g", " "); + t = (t || "").replace(/\n/g, " ").replace(/(-\s)/g, "-").replace("/(\s\s)/g", " "); let s = new o(t); if (e.simplification) { const t = new r(s.linearPoints, s.closed).fit(e.simplification); diff --git a/d2renderers/d2sketch/sketch_test.go b/d2renderers/d2sketch/sketch_test.go index e033bedfe..c2b8dadea 100644 --- a/d2renderers/d2sketch/sketch_test.go +++ b/d2renderers/d2sketch/sketch_test.go @@ -314,7 +314,7 @@ func run(t *testing.T, tc testCase) { diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{ Ruler: ruler, ThemeID: 0, - Layout: d2dagrelayout.Layout, + Layout: d2dagrelayout.DefaultLayout, FontFamily: go2.Pointer(d2fonts.HandDrawn), }) if !tassert.Nil(t, err) { diff --git a/d2renderers/d2sketch/testdata/all_shapes/sketch.exp.svg b/d2renderers/d2sketch/testdata/all_shapes/sketch.exp.svg index 5b8429973..efb2c8aba 100644 --- a/d2renderers/d2sketch/testdata/all_shapes/sketch.exp.svg +++ b/d2renderers/d2sketch/testdata/all_shapes/sketch.exp.svg @@ -1,8 +1,9 @@ -rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud - +rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud + + -ab - +ab + + -wintersummersnowsun - +wintersummersnowsun + + @@ -42,8 +63,8 @@ width="622" height="568" viewBox="-100 -100 622 568"> -ab hello - +ab hello + + @@ -62,8 +83,8 @@ width="1025" height="660" viewBox="-100 -100 1025 660">
  • Served data logging
  • GraphQLFederated Strato Column

    Tweet/user content hydration, visibility filtering

    -
    TLS-API (being deprecated)CrMixerEarlyBirdUtagSpaceCommunities iPhone webHTTP AndroidThrift RPC Candidate FetchFeature HydrationCandidate sources - +TLS-API (being deprecated)CrMixerEarlyBirdUtagSpaceCommunities iPhone webHTTP AndroidThrift RPC Candidate FetchFeature HydrationCandidate sources + diff --git a/d2renderers/d2svg/appendix/appendix.go b/d2renderers/d2svg/appendix/appendix.go index f25dc1c02..7237075ab 100644 --- a/d2renderers/d2svg/appendix/appendix.go +++ b/d2renderers/d2svg/appendix/appendix.go @@ -153,6 +153,10 @@ func generateAppendix(diagram *d2target.Diagram, ruler *textmeasure.Ruler, svg s } } } + if len(lines) == 0 { + return "", 0, 0 + } + totalHeight += SPACER return fmt.Sprintf(`%s `, tl.X, br.Y, (br.X - tl.X), strings.Join(lines, "\n")), maxWidth, totalHeight @@ -182,5 +186,5 @@ func generateLine(i, y int, text string, ruler *textmeasure.Ruler) (string, int, line += fmt.Sprintf(`%s`, ICON_RADIUS*3, y, FONT_SIZE, d2svg.RenderText(text, ICON_RADIUS*3, float64(dims.Height))) - return line, dims.Width + ICON_RADIUS*3, dims.Height + return line, dims.Width + ICON_RADIUS*3, go2.IntMax(dims.Height, ICON_RADIUS*2) } diff --git a/d2renderers/d2svg/appendix/appendix_test.go b/d2renderers/d2svg/appendix/appendix_test.go index a4105c38e..74a6f327f 100644 --- a/d2renderers/d2svg/appendix/appendix_test.go +++ b/d2renderers/d2svg/appendix/appendix_test.go @@ -121,7 +121,7 @@ func run(t *testing.T, tc testCase) { diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{ Ruler: ruler, ThemeID: 0, - Layout: d2dagrelayout.Layout, + Layout: d2dagrelayout.DefaultLayout, }) if !tassert.Nil(t, err) { return diff --git a/d2renderers/d2svg/appendix/testdata/diagram_wider_than_tooltip/sketch.exp.svg b/d2renderers/d2svg/appendix/testdata/diagram_wider_than_tooltip/sketch.exp.svg index 6c5e1e58b..b307fd5d2 100644 --- a/d2renderers/d2svg/appendix/testdata/diagram_wider_than_tooltip/sketch.exp.svg +++ b/d2renderers/d2svg/appendix/testdata/diagram_wider_than_tooltip/sketch.exp.svg @@ -1,8 +1,9 @@ customerissuerstore1Like starbucks or somethingacquirer2I'm not sure what this isnetworkcustomer bankstore bankinitial transactionpayment processor behind the scenessimplified 1 banana please$10 dollarsthinking: wow, inflationchecks bank accountSavings: $11I can do that, here's my cardRun this cardProcess to card issuerProcess this payment$10 debit$10 creditAn error in judgement is about to occur + @@ -51,8 +72,8 @@ width="1959" height="2297" viewBox="-175 -47 1959 2297">1Like starbucks or something -2I'm not sure what this is +}]]>1Like starbucks or something +2I'm not sure what this is x1y2Gee, I feel kind of LIGHT in the head now, -knowing I can't make my satellite dish PAYMENTS!3 - +x1y2Gee, I feel kind of LIGHT in the head now, +knowing I can't make my satellite dish PAYMENTS!3 + 1https://d2lang.com -2Gee, I feel kind of LIGHT in the head now,knowing I can't make my satellite dish PAYMENTS! -3https://terrastruct.com +}]]>1https://d2lang.com +2Gee, I feel kind of LIGHT in the head now,knowing I can't make my satellite dish PAYMENTS! +3https://terrastruct.com x1Total abstinence is easier than perfect moderationy2Gee, I feel kind of LIGHT in the head now, -knowing I can't make my satellite dish PAYMENTS! - +x1Total abstinence is easier than perfect moderationy2Gee, I feel kind of LIGHT in the head now, +knowing I can't make my satellite dish PAYMENTS! + 1Total abstinence is easier than perfect moderation -2Gee, I feel kind of LIGHT in the head now,knowing I can't make my satellite dish PAYMENTS! +}]]>1Total abstinence is easier than perfect moderation +2Gee, I feel kind of LIGHT in the head now,knowing I can't make my satellite dish PAYMENTS! `) } +//go:embed fitToScreen.js +var fitToScreenScript string + // TODO minify output at end func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) { var sketchRunner *d2sketch.Runner @@ -1082,6 +1128,10 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) { ]]> `, styleCSS, styleCSS2)) + // this script won't run in --watch mode because script tags are ignored when added via el.innerHTML = element + // https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML + buf.WriteString(fmt.Sprintf(``, fitToScreenScript)) + hasMarkdown := false for _, s := range diagram.Shapes { if s.Label != "" && s.Type == d2target.ShapeText { diff --git a/d2renderers/d2svg/fitToScreen.js b/d2renderers/d2svg/fitToScreen.js new file mode 100644 index 000000000..51e6d6ea4 --- /dev/null +++ b/d2renderers/d2svg/fitToScreen.js @@ -0,0 +1,20 @@ +window.addEventListener("DOMContentLoaded", () => { + if (document.documentElement.getAttribute("id") !== "d2-svg") { + return; + } + const svgEl = document.documentElement; + let width = parseInt(svgEl.getAttribute("width"), 10); + let height = parseInt(svgEl.getAttribute("height"), 10); + let ratio; + if (width > height) { + if (width > window.innerWidth) { + ratio = window.innerWidth / width; + } + } else if (height > window.innerHeight) { + ratio = window.innerHeight / height; + } + if (ratio) { + svgEl.setAttribute("width", width * ratio - 16); + svgEl.setAttribute("height", height * ratio - 16); + } +}); diff --git a/d2target/d2target.go b/d2target/d2target.go index 90974c7f1..b426adfd9 100644 --- a/d2target/d2target.go +++ b/d2target/d2target.go @@ -55,10 +55,16 @@ func (diagram Diagram) BoundingBox() (topLeft, bottomRight Point) { y2 := int(math.MinInt32) for _, targetShape := range diagram.Shapes { - x1 = go2.Min(x1, targetShape.Pos.X) - y1 = go2.Min(y1, targetShape.Pos.Y) - x2 = go2.Max(x2, targetShape.Pos.X+targetShape.Width) - y2 = go2.Max(y2, targetShape.Pos.Y+targetShape.Height) + x1 = go2.Min(x1, targetShape.Pos.X-targetShape.StrokeWidth) + y1 = go2.Min(y1, targetShape.Pos.Y-targetShape.StrokeWidth) + x2 = go2.Max(x2, targetShape.Pos.X+targetShape.Width+targetShape.StrokeWidth) + y2 = go2.Max(y2, targetShape.Pos.Y+targetShape.Height+targetShape.StrokeWidth) + + if targetShape.Tooltip != "" || targetShape.Link != "" { + // 16 is the icon radius + y1 = go2.Min(y1, targetShape.Pos.Y-targetShape.StrokeWidth-16) + x2 = go2.Max(x2, targetShape.Pos.X+targetShape.StrokeWidth+targetShape.Width+16) + } if targetShape.Label != "" { labelPosition := label.Position(targetShape.LabelPosition) @@ -274,6 +280,12 @@ const ( // For fat arrows LineArrowhead Arrowhead = "line" + + // Crows feet notation + CfOne Arrowhead = "cf-one" + CfMany Arrowhead = "cf-many" + CfOneRequired Arrowhead = "cf-one-required" + CfManyRequired Arrowhead = "cf-many-required" ) var Arrowheads = map[string]struct{}{ @@ -282,6 +294,10 @@ var Arrowheads = map[string]struct{}{ string(TriangleArrowhead): {}, string(DiamondArrowhead): {}, string(FilledDiamondArrowhead): {}, + string(CfOne): {}, + string(CfMany): {}, + string(CfOneRequired): {}, + string(CfManyRequired): {}, } func ToArrowhead(arrowheadType string, filled bool) Arrowhead { @@ -293,6 +309,14 @@ func ToArrowhead(arrowheadType string, filled bool) Arrowhead { return DiamondArrowhead case string(ArrowArrowhead): return ArrowArrowhead + case string(CfOne): + return CfOne + case string(CfMany): + return CfMany + case string(CfOneRequired): + return CfOneRequired + case string(CfManyRequired): + return CfManyRequired default: return TriangleArrowhead } diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 0ffe8520c..2b28972ab 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -17,6 +17,20 @@ Most of D2's CI is open sourced in its own workflows. E.g. run `./make.sh fmt` to run the formatter. Please make sure all CI is passing for any PRs. +Most of the CI scripts rely on a submodule shared between many D2 repositories: +[https://github.com/terrastruct/ci](https://github.com/terrastruct/ci). You should fetch +the submodule whenever it differs: + +```sh +git submodule update --recursive +``` + +If running for the first time for a repo (e.g. new clone), add `--init`: + +```sh +git submodule update --init --recursive +``` + ## Flow The simplified D2 flow at a package level looks like: @@ -25,6 +39,9 @@ The simplified D2 flow at a package level looks like: ## Logistics +- Use Go 1.18. Go 1.19's autofmt inexplicably strips spacing from ASCII art in comments. +- Please sign your commits + ([https://github.com/terrastruct/d2/pull/557#issuecomment-1367468730](https://github.com/terrastruct/d2/pull/557#issuecomment-1367468730)). - D2 uses Issues as TODOs. No auto-closing on staleness. - Branch off `master`. - Prefix pull request titles with a short descriptor of the domain, e.g. `d2renderer: Add diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 99793ad0e..5c20aa1bd 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -179,7 +179,7 @@ Remember, you need at least Go v1.18 ## Windows -We have prebuilt releases of d2 available for Windows via `.msi` installers. The installer +We have prebuilt [releases](https://github.com/terrastruct/d2/releases) of d2 available for Windows via `.msi` installers. The installer will add the `d2` binary to your `$PATH` so that you can execute `d2` in `cmd.exe` or `pwsh.exe`. diff --git a/docs/assets/flow.svg b/docs/assets/flow.svg index 01f73045c..fbb675e9e 100644 --- a/docs/assets/flow.svg +++ b/docs/assets/flow.svg @@ -1,5 +1,6 @@ lambda-build.yamllambda-deploy.yamlapollo-deploy.yamlPush to main branchGitHub ActionsS3TerraformAWSManual TriggerGitHub ActionsAWSApollo RepoGitHub ActionsAWS TriggersBuilds zip and pushes it Pulls zip to deployChanges live lambdasLaunchesBuilds zippushes them to S3. Deploys lambdasusing TerraformTriggered manually/push to master test test test test test test testtest + diff --git a/e2etests/testdata/regression/elk_alignment/elk/sketch.exp.svg b/e2etests/testdata/regression/elk_alignment/elk/sketch.exp.svg index 593ff3bb8..4395498b0 100644 --- a/e2etests/testdata/regression/elk_alignment/elk/sketch.exp.svg +++ b/e2etests/testdata/regression/elk_alignment/elk/sketch.exp.svg @@ -1,8 +1,9 @@ lambda-build.yamllambda-deploy.yamlapollo-deploy.yamlPush to main branchGitHub ActionsS3TerraformAWSManual TriggerGitHub ActionsAWSApollo RepoGitHub ActionsAWS TriggersBuilds zip and pushes it Pulls zip to deployChanges live lambdasLaunchesBuilds zippushes them to S3. Deploys lambdasusing TerraformTriggered manually/push to master test test test test test test testtest + diff --git a/e2etests/testdata/regression/elk_img_empty_label_panic/dagre/sketch.exp.svg b/e2etests/testdata/regression/elk_img_empty_label_panic/dagre/sketch.exp.svg index 3726c5c2d..41697315e 100644 --- a/e2etests/testdata/regression/elk_img_empty_label_panic/dagre/sketch.exp.svg +++ b/e2etests/testdata/regression/elk_img_empty_label_panic/dagre/sketch.exp.svg @@ -1,8 +1,9 @@ + \ No newline at end of file diff --git a/e2etests/testdata/regression/elk_img_empty_label_panic/elk/sketch.exp.svg b/e2etests/testdata/regression/elk_img_empty_label_panic/elk/sketch.exp.svg index 81e2051b4..2ca3fb21b 100644 --- a/e2etests/testdata/regression/elk_img_empty_label_panic/elk/sketch.exp.svg +++ b/e2etests/testdata/regression/elk_img_empty_label_panic/elk/sketch.exp.svg @@ -1,8 +1,9 @@ + \ No newline at end of file diff --git a/e2etests/testdata/regression/elk_order/dagre/sketch.exp.svg b/e2etests/testdata/regression/elk_order/dagre/sketch.exp.svg index 64e3079f2..373f07923 100644 --- a/e2etests/testdata/regression/elk_order/dagre/sketch.exp.svg +++ b/e2etests/testdata/regression/elk_order/dagre/sketch.exp.svg @@ -1,8 +1,9 @@ my network + my network - +my network + foofoobarabcd +foofoobarabcd foofoobarabcd +foofoobarabcd AB - +AB + AB - +AB + b - +b + b - +b + sql_table_overflowshort +sql_table_overflowshort loooooooooooooooooooong loooooooooooooooooooong short @@ -26,8 +47,8 @@ width="1388" height="308" viewBox="-100 -100 1388 308"> - +FK + sql_table_overflowshort +sql_table_overflowshort loooooooooooooooooooong loooooooooooooooooooong short @@ -26,8 +47,8 @@ width="1348" height="308" viewBox="-88 -88 1348 308"> - +FK + - +- num int- timeout @@ -42,8 +63,8 @@ width="622" height="1024" viewBox="-100 -100 622 1024">:= 5 := a + 7 -fmt.Printf("%d", b) - +fmt.Printf("%d", b) + - +- num int- timeout @@ -42,8 +63,8 @@ width="622" height="1024" viewBox="-88 -88 622 1024">:= 5 := a + 7 -fmt.Printf("%d", b) - +fmt.Printf("%d", b) + abc - +abc + abc - +abc + ab - +ab + ab - +ab + acbd - +acbd + acbd - +acbd + ab hello - +ab hello + ab hello - +ab hello + + \ No newline at end of file diff --git a/e2etests/testdata/sanity/empty/elk/sketch.exp.svg b/e2etests/testdata/sanity/empty/elk/sketch.exp.svg index 243722dc6..5169c50f1 100644 --- a/e2etests/testdata/sanity/empty/elk/sketch.exp.svg +++ b/e2etests/testdata/sanity/empty/elk/sketch.exp.svg @@ -1,5 +1,6 @@ \ No newline at end of file diff --git a/e2etests/testdata/stable/all_shapes/dagre/sketch.exp.svg b/e2etests/testdata/stable/all_shapes/dagre/sketch.exp.svg index 0ff95d243..da44f0f40 100644 --- a/e2etests/testdata/stable/all_shapes/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/all_shapes/dagre/sketch.exp.svg @@ -1,8 +1,9 @@ rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud + rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud - +rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud + rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud - +rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud + rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud - +rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud + + @@ -26,8 +47,8 @@ width="1539" height="824" viewBox="-100 -100 1539 824"> -rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud - +rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud + + @@ -26,8 +47,8 @@ width="1339" height="824" viewBox="-88 -88 1339 824"> -rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud - +rectanglesquarepageparallelogramdocumentcylinderqueuepackagestepcalloutstored_datapersondiamondovalcirclehexagoncloud + cba * - +cba * + cba * - +cba * + ab To err is human, to moo bovine1* - +ab To err is human, to moo bovine1* + ab To err is human, to moo bovine1* - +ab To err is human, to moo bovine1* + abcdefghijklmno - +abcdefghijklmno + abcdefghijklmno - +abcdefghijklmno + aaadddeeebbbccc111 222 - +aaadddeeebbbccc111 222 + aaadddeeebbbccc111 222 - +aaadddeeebbbccc111 222 + aabbllmmnnoocciikkddgghhjjeeff1122 334455667788 + diff --git a/e2etests/testdata/stable/chaos2/elk/sketch.exp.svg b/e2etests/testdata/stable/chaos2/elk/sketch.exp.svg index 691cad438..e616b37b0 100644 --- a/e2etests/testdata/stable/chaos2/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/chaos2/elk/sketch.exp.svg @@ -1,8 +1,9 @@ abcd + abc - +abc + abc - +abc + BatchManager- +BatchManager- num int- timeout @@ -30,8 +51,8 @@ width="622" height="568" viewBox="-100 -100 622 568"> - +void + BatchManager- +BatchManager- num int- timeout @@ -30,8 +51,8 @@ width="622" height="568" viewBox="-88 -88 622 568"> - +void + // RegisterHash registers a function that returns a new instance of the given +// RegisterHash registers a function that returns a new instance of the given // hash function. This is intended to be called from the init function in // packages that implement hash functions. func RegisterHash(h Hash, f func() hash.Hash) { @@ -26,8 +47,8 @@ width="955" height="818" viewBox="-100 -100 955 818">        panic("crypto: RegisterHash of unknown hash function")     }     hashes[h] = f -}xy - +}xy + // RegisterHash registers a function that returns a new instance of the given +// RegisterHash registers a function that returns a new instance of the given // hash function. This is intended to be called from the init function in // packages that implement hash functions. func RegisterHash(h Hash, f func() hash.Hash) { @@ -26,8 +47,8 @@ width="955" height="818" viewBox="-88 -88 955 818">        panic("crypto: RegisterHash of unknown hash function")     }     hashes[h] = f -}xy - +}xy + acfbdhg - +acfbdhg + acfbdhg - +acfbdhg + \ No newline at end of file diff --git a/e2etests/testdata/stable/crow_foot_arrowhead/elk/board.exp.json b/e2etests/testdata/stable/crow_foot_arrowhead/elk/board.exp.json new file mode 100644 index 000000000..d87fec01c --- /dev/null +++ b/e2etests/testdata/stable/crow_foot_arrowhead/elk/board.exp.json @@ -0,0 +1,531 @@ +{ + "name": "", + "fontFamily": "SourceSansPro", + "shapes": [ + { + "id": "a", + "type": "", + "pos": { + "x": 12, + "y": 12 + }, + "width": 113, + "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": "a", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 13, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "b", + "type": "", + "pos": { + "x": 12, + "y": 238 + }, + "width": 113, + "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": "b", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 13, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "c", + "type": "", + "pos": { + "x": 145, + "y": 12 + }, + "width": 113, + "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": "c", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 13, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "d", + "type": "", + "pos": { + "x": 145, + "y": 238 + }, + "width": 114, + "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": "d", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 14, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "f", + "type": "", + "pos": { + "x": 165, + "y": 464 + }, + "width": 111, + "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": "f", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 11, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "g", + "type": "", + "pos": { + "x": 278, + "y": 12 + }, + "width": 114, + "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": "g", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 14, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "h", + "type": "", + "pos": { + "x": 279, + "y": 238 + }, + "width": 113, + "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": "h", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 13, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + }, + { + "id": "e", + "type": "", + "pos": { + "x": 412, + "y": 238 + }, + "width": 113, + "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": "e", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#0A0F25", + "italic": false, + "bold": true, + "underline": false, + "labelWidth": 13, + "labelHeight": 26, + "labelPosition": "INSIDE_MIDDLE_CENTER", + "zIndex": 0, + "level": 1 + } + ], + "connections": [ + { + "id": "(a <-> b)[0]", + "src": "a", + "srcArrow": "cf-many", + "srcLabel": "", + "dst": "b", + "dstArrow": "cf-many", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 3, + "stroke": "#20222a", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 68.5, + "y": 138 + }, + { + "x": 68.5, + "y": 238 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(c <-> d)[0]", + "src": "c", + "srcArrow": "cf-many-required", + "srcLabel": "", + "dst": "d", + "dstArrow": "cf-many-required", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 1, + "stroke": "orange", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 202, + "y": 138 + }, + { + "x": 202, + "y": 238 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(d <-> f)[0]", + "src": "d", + "srcArrow": "cf-many-required", + "srcLabel": "", + "dst": "f", + "dstArrow": "cf-many-required", + "dstLabel": "", + "opacity": 1, + "strokeDash": 0, + "strokeWidth": 1, + "stroke": "orange", + "label": "", + "fontSize": 16, + "fontFamily": "DEFAULT", + "language": "", + "color": "#676C7E", + "italic": true, + "bold": false, + "underline": false, + "labelWidth": 0, + "labelHeight": 0, + "labelPosition": "", + "labelPercentage": 0, + "route": [ + { + "x": 202, + "y": 364 + }, + { + "x": 202, + "y": 464 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(g <-> h)[0]", + "src": "g", + "srcArrow": "cf-one", + "srcLabel": "", + "dst": "h", + "dstArrow": "cf-one", + "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": 335.5, + "y": 138 + }, + { + "x": 335.5, + "y": 238 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + }, + { + "id": "(e <-> f)[0]", + "src": "e", + "srcArrow": "cf-one-required", + "srcLabel": "", + "dst": "f", + "dstArrow": "cf-one-required", + "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": 468.5, + "y": 364 + }, + { + "x": 468.5, + "y": 414 + }, + { + "x": 239, + "y": 414 + }, + { + "x": 239, + "y": 464 + } + ], + "animated": false, + "tooltip": "", + "icon": null, + "zIndex": 0 + } + ] +} diff --git a/e2etests/testdata/stable/crow_foot_arrowhead/elk/sketch.exp.svg b/e2etests/testdata/stable/crow_foot_arrowhead/elk/sketch.exp.svg new file mode 100644 index 000000000..9e900f499 --- /dev/null +++ b/e2etests/testdata/stable/crow_foot_arrowhead/elk/sketch.exp.svg @@ -0,0 +1,52 @@ + +abcdfghe + + + \ No newline at end of file diff --git a/e2etests/testdata/stable/dense/dagre/sketch.exp.svg b/e2etests/testdata/stable/dense/dagre/sketch.exp.svg index 08ed658e2..de442c896 100644 --- a/e2etests/testdata/stable/dense/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/dense/dagre/sketch.exp.svg @@ -1,8 +1,9 @@ abcdefghijklmnopq + abcdefghijklmnopq - +abcdefghijklmnopq + finallyatreeandnodessomemoremanythenhereyouhavehierarchyanotherofnestingtreesatreeinsidehierarchyroot - +finallyatreeandnodessomemoremanythenhereyouhavehierarchyanotherofnestingtreesatreeinsidehierarchyroot + finallyatreeandnodessomemoremanythenhereyouhavehierarchyanotherofnestingtreesatreeinsidehierarchyroot - +finallyatreeandnodessomemoremanythenhereyouhavehierarchyanotherofnestingtreesatreeinsidehierarchyroot + bacde21345abcde - +bacde21345abcde + bacde21345abcde - +bacde21345abcde + alphabeta gamma - +alphabeta gamma + alphabeta gamma - +alphabeta gamma + size XSsize Ssize Msize Lsize XLsize XXLsize XXXLcustom 8custom 12custom 18custom 21custom 64 custom 10custom 15custom 48 - +size XSsize Ssize Msize Lsize XLsize XXLsize XXXLcustom 8custom 12custom 18custom 21custom 64 custom 10custom 15custom 48 + diff --git a/e2etests/testdata/stable/font_sizes/elk/sketch.exp.svg b/e2etests/testdata/stable/font_sizes/elk/sketch.exp.svg index f8f67a3b1..2d5f2fe91 100644 --- a/e2etests/testdata/stable/font_sizes/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/font_sizes/elk/sketch.exp.svg @@ -1,8 +1,9 @@ size XSsize Ssize Msize Lsize XLsize XXLsize XXXLcustom 8custom 12custom 18custom 21custom 64 custom 10custom 15custom 48 + diff --git a/e2etests/testdata/stable/giant_markdown_test/dagre/sketch.exp.svg b/e2etests/testdata/stable/giant_markdown_test/dagre/sketch.exp.svg index b257f8591..0072c98ec 100644 --- a/e2etests/testdata/stable/giant_markdown_test/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/giant_markdown_test/dagre/sketch.exp.svg @@ -1,8 +1,9 @@

    Code

    Unlike a pre-formatted code block, a code span indicates code within a normal paragraph. For example:

    -ab - +ab + hello - +hello + hello - +hello + ab - +ab + ab - +ab + aabbccddllffwwyynniijjkkssuurmeemmmmgghhzzooppqqrrttvvxxabac 123456 - +aabbccddllffwwyynniijjkkssuurmeemmmmgghhzzooppqqrrttvvxxabac 123456 + diff --git a/e2etests/testdata/stable/investigate/elk/sketch.exp.svg b/e2etests/testdata/stable/investigate/elk/sketch.exp.svg index 81797bd00..fa10408c1 100644 --- a/e2etests/testdata/stable/investigate/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/investigate/elk/sketch.exp.svg @@ -1,8 +1,9 @@ aabbccddllffwwyynniijjkkssuurmeemmmmgghhzzooppqqrrttvvxxabac 123456 + diff --git a/e2etests/testdata/stable/large_arch/dagre/sketch.exp.svg b/e2etests/testdata/stable/large_arch/dagre/sketch.exp.svg index 482b251d0..2c9fa385f 100644 --- a/e2etests/testdata/stable/large_arch/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/large_arch/dagre/sketch.exp.svg @@ -1,8 +1,9 @@ abcdefghiqrjmnoszaabbeeffggklptuwxyccddv + abcdefghiqrjmnoszaabbeeffggklptuwxyccddv - +abcdefghiqrjmnoszaabbeeffggklptuwxyccddv + mixed togethersugarsolution we get + mixed togethersugarsolution we get +

    Another item in the same list.

    -ab - +ab + x +x @@ -56,8 +77,8 @@ knowing I can't make my satellite dish PAYMENTS! - + + x +x @@ -56,8 +77,8 @@ knowing I can't make my satellite dish PAYMENTS! - + + ab + ab + markdown

    Lorem ipsum dolor sit amet, consectetur adipiscing elit,
    sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

    -
    - + + markdown

    Lorem ipsum dolor sit amet, consectetur adipiscing elit,
    sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

    -
    - + + markdown

    Lorem ipsum dolor sit amet, consectetur adipiscing elit,
    sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

    -
    - + + markdown

    Lorem ipsum dolor sit amet, consectetur adipiscing elit,
    sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

    -
    - + +

    code

    -
    ab - +ab +

    code

    -
    ab - +ab + thisgoesmultiple lines - +thisgoesmultiple lines + thisgoesmultiple lines - +thisgoesmultiple lines + abcdefghijklmnopqrstuvw - +abcdefghijklmnopqrstuvw + abcdefghijklmnopqrstuvw - +abcdefghijklmnopqrstuvw + abcdefghijklmnopqrstu - +abcdefghijklmnopqrstu + abcdefghijklmnopqrstu - +abcdefghijklmnopqrstu + Foo Baz12hello - +Foo Baz12hello + Foo Baz12hello - +Foo Baz12hello + acdefgbh - +acdefgbh + acdefgbh - +acdefgbh + topabcbottomstartend - +topabcbottomstartend + topabcbottomstartend - +topabcbottomstartend + rootcontainerrootleftrightrootinnerrootinnerleftrightleftright to inner leftto inner rightto inner leftto inner rightto left container rootto right container root - +rootcontainerrootleftrightrootinnerrootinnerleftrightleftright to inner leftto inner rightto inner leftto inner rightto left container rootto right container root + diff --git a/e2etests/testdata/stable/overlapping_image_container_labels/elk/sketch.exp.svg b/e2etests/testdata/stable/overlapping_image_container_labels/elk/sketch.exp.svg index 9505c6dec..e27957884 100644 --- a/e2etests/testdata/stable/overlapping_image_container_labels/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/overlapping_image_container_labels/elk/sketch.exp.svg @@ -1,8 +1,9 @@ rootcontainerrootleftrightrootinnerrootinnerleftrightleftright to inner leftto inner rightto inner leftto inner rightto left container rootto right container root + diff --git a/e2etests/testdata/stable/p/dagre/sketch.exp.svg b/e2etests/testdata/stable/p/dagre/sketch.exp.svg index 6aba7026c..caec929ea 100644 --- a/e2etests/testdata/stable/p/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/p/dagre/sketch.exp.svg @@ -1,8 +1,9 @@

    A code block continues until it reaches a line that is not indented (or the end of the article).

    -ab - +ab + xyz hello - +xyz hello + xyz hello - +xyz hello + an actor with a really long label that will break everythinganactorwithareallylonglabelthatwillbreakeverythingsimplea short onefar awaywhat if there were no labels between this actor and the previous one shortlong label for testing purposes and it must be really, really longshortthis should span many actors lifelines so we know how it will look like when redering a long label over many actorslong label for testing purposes and it must be really, really long - +an actor with a really long label that will break everythinganactorwithareallylonglabelthatwillbreakeverythingsimplea short onefar awaywhat if there were no labels between this actor and the previous one shortlong label for testing purposes and it must be really, really longshortthis should span many actors lifelines so we know how it will look like when redering a long label over many actorslong label for testing purposes and it must be really, really long + diff --git a/e2etests/testdata/stable/sequence_diagram_actor_distance/elk/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_actor_distance/elk/sketch.exp.svg index f0302fd8e..da52a5275 100644 --- a/e2etests/testdata/stable/sequence_diagram_actor_distance/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagram_actor_distance/elk/sketch.exp.svg @@ -1,8 +1,9 @@ an actor with a really long label that will break everythinganactorwithareallylonglabelthatwillbreakeverythingsimplea short onefar awaywhat if there were no labels between this actor and the previous one shortlong label for testing purposes and it must be really, really longshortthis should span many actors lifelines so we know how it will look like when redering a long label over many actorslong label for testing purposes and it must be really, really long + diff --git a/e2etests/testdata/stable/sequence_diagram_all_shapes/dagre/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_all_shapes/dagre/sketch.exp.svg index 2b86d5324..b3642eb08 100644 --- a/e2etests/testdata/stable/sequence_diagram_all_shapes/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagram_all_shapes/dagre/sketch.exp.svg @@ -1,8 +1,9 @@ a labelblabelsa class+ public() bool void- private() int @@ -28,8 +49,8 @@ width="5177" height="2984" viewBox="-76 -26 5177 2984"> result := callThisFunction(obj, 5) midthis sideother side - + result := callThisFunction(obj, 5) midthis sideother side + a labelblabelsa class+ +a labelblabelsa class+ public() bool void- private() int @@ -28,8 +49,8 @@ width="5177" height="2984" viewBox="-76 -26 5177 2984"> result := callThisFunction(obj, 5) midthis sideother side - + result := callThisFunction(obj, 5) midthis sideother side + alicebob what does it mean to be well-adjustedThe ability to play bridge or golf as if they were games - +alicebob what does it mean to be well-adjustedThe ability to play bridge or golf as if they were games + alicebob what does it mean to be well-adjustedThe ability to play bridge or golf as if they were games - +alicebob what does it mean to be well-adjustedThe ability to play bridge or golf as if they were games + abcdggggroup 1group bchoonested guy lalaeyokayokaywhat would arnold saythis note - +abcdggggroup 1group bchoonested guy lalaeyokayokaywhat would arnold saythis note + diff --git a/e2etests/testdata/stable/sequence_diagram_groups/elk/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_groups/elk/sketch.exp.svg index d3ed45a1a..65bec6a73 100644 --- a/e2etests/testdata/stable/sequence_diagram_groups/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagram_groups/elk/sketch.exp.svg @@ -1,8 +1,9 @@ abcdggggroup 1group bchoonested guy lalaeyokayokaywhat would arnold saythis note + diff --git a/e2etests/testdata/stable/sequence_diagram_long_note/dagre/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_long_note/dagre/sketch.exp.svg index 8cc670b3d..14ef25227 100644 --- a/e2etests/testdata/stable/sequence_diagram_long_note/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagram_long_note/dagre/sketch.exp.svg @@ -1,8 +1,9 @@ ba a note here to remember that padding must consider notes toojustalongnotehere + ba a note here to remember that padding must consider notes toojustalongnotehere - +ba a note here to remember that padding must consider notes toojustalongnotehere + abjust an actorthis is a message groupaltand this is a nested message groupcase 1case 2case 3case 4what about more nestingcrazy townwhoa a notea note here to remember that padding must consider notes toojustalongnotehere - +abjust an actorthis is a message groupaltand this is a nested message groupcase 1case 2case 3case 4what about more nestingcrazy townwhoa a notea note here to remember that padding must consider notes toojustalongnotehere + diff --git a/e2etests/testdata/stable/sequence_diagram_nested_groups/elk/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_nested_groups/elk/sketch.exp.svg index 48b328e86..dcb85eeaa 100644 --- a/e2etests/testdata/stable/sequence_diagram_nested_groups/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagram_nested_groups/elk/sketch.exp.svg @@ -1,8 +1,9 @@ abjust an actorthis is a message groupaltand this is a nested message groupcase 1case 2case 3case 4what about more nestingcrazy townwhoa a notea note here to remember that padding must consider notes toojustalongnotehere + diff --git a/e2etests/testdata/stable/sequence_diagram_nested_span/dagre/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_nested_span/dagre/sketch.exp.svg index 9fb361e64..4d782f1f0 100644 --- a/e2etests/testdata/stable/sequence_diagram_nested_span/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagram_nested_span/dagre/sketch.exp.svg @@ -1,8 +1,9 @@ scoreritemResponseitemessayRubricconceptitemOutcome + scoreritemResponseitemessayRubricconceptitemOutcome - +scoreritemResponseitemessayRubricconceptitemOutcome + abcd okayexplanationanother explanationSome one who believes imaginary things appear right before your i's.The earth is like a tiny grain of sand, only much, much heavier - +abcd okayexplanationanother explanationSome one who believes imaginary things appear right before your i's.The earth is like a tiny grain of sand, only much, much heavier + abcd okayexplanationanother explanationSome one who believes imaginary things appear right before your i's.The earth is like a tiny grain of sand, only much, much heavier - +abcd okayexplanationanother explanationSome one who believes imaginary things appear right before your i's.The earth is like a tiny grain of sand, only much, much heavier + How this is renderedCLId2astd2compilerd2layoutd2exporterd2themesd2rendererd2sequencelayoutd2dagrelayoutonly if root is not sequence 'How this is rendered: {...}'tokenized ASTcompile ASTobjects and edgesrun layout enginesrun engine on shape: sequence_diagram, temporarily removerun core engine on rest add back in sequence diagramsdiagram with correct positions and dimensionsexport diagram with chosen theme and rendererget theme stylesrender to SVGresulting SVGmeasurements also take place +How this is renderedCLId2astd2compilerd2layoutd2exporterd2themesd2rendererd2sequencelayoutd2dagrelayoutonly if root is not sequence 'How this is rendered: {...}'tokenized ASTcompile ASTobjects and edgesrun layout enginesrun engine on shape: sequence_diagram, temporarily removerun core engine on rest add back in sequence diagramsdiagram with correct positions and dimensionsexport diagram with chosen theme and rendererget theme stylesrender to SVGresulting SVGmeasurements also take place diff --git a/e2etests/testdata/stable/sequence_diagram_real/elk/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_real/elk/sketch.exp.svg index 5f4dd359a..d35f83ea3 100644 --- a/e2etests/testdata/stable/sequence_diagram_real/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagram_real/elk/sketch.exp.svg @@ -1,5 +1,6 @@ How this is renderedCLId2astd2compilerd2layoutd2exporterd2themesd2rendererd2sequencelayoutd2dagrelayoutonly if root is not sequence 'How this is rendered: {...}'tokenized ASTcompile ASTobjects and edgesrun layout enginesrun engine on shape: sequence_diagram, temporarily removerun core engine on rest add back in sequence diagramsdiagram with correct positions and dimensionsexport diagram with chosen theme and rendererget theme stylesrender to SVGresulting SVGmeasurements also take place diff --git a/e2etests/testdata/stable/sequence_diagram_self_edges/dagre/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_self_edges/dagre/sketch.exp.svg index f947663cd..b9ac8aeed 100644 --- a/e2etests/testdata/stable/sequence_diagram_self_edges/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagram_self_edges/dagre/sketch.exp.svg @@ -1,8 +1,9 @@ ab a self edge herebetween actorsto descendantto deeper descendantto parentactor + diff --git a/e2etests/testdata/stable/sequence_diagram_self_edges/elk/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_self_edges/elk/sketch.exp.svg index f947663cd..b9ac8aeed 100644 --- a/e2etests/testdata/stable/sequence_diagram_self_edges/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagram_self_edges/elk/sketch.exp.svg @@ -1,8 +1,9 @@ ab a self edge herebetween actorsto descendantto deeper descendantto parentactor + diff --git a/e2etests/testdata/stable/sequence_diagram_simple/dagre/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_simple/dagre/sketch.exp.svg index d0e6006a2..5b7083c7b 100644 --- a/e2etests/testdata/stable/sequence_diagram_simple/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagram_simple/dagre/sketch.exp.svg @@ -1,8 +1,9 @@ AlicelinebreakerBobdbqueueanoddservicewithanameinmultiple lines Authentication Requestmake request for something that is quite far away and requires a really long label to take all the space between the objectsvalidate credentialsAuthentication ResponseAnother authentication Requestdo it later storedAnother authentication Response + diff --git a/e2etests/testdata/stable/sequence_diagram_simple/elk/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_simple/elk/sketch.exp.svg index d0e6006a2..5b7083c7b 100644 --- a/e2etests/testdata/stable/sequence_diagram_simple/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagram_simple/elk/sketch.exp.svg @@ -1,8 +1,9 @@ AlicelinebreakerBobdbqueueanoddservicewithanameinmultiple lines Authentication Requestmake request for something that is quite far away and requires a really long label to take all the space between the objectsvalidate credentialsAuthentication ResponseAnother authentication Requestdo it later storedAnother authentication Response + diff --git a/e2etests/testdata/stable/sequence_diagram_span/dagre/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_span/dagre/sketch.exp.svg index aa3321ec2..39b8a5c94 100644 --- a/e2etests/testdata/stable/sequence_diagram_span/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagram_span/dagre/sketch.exp.svg @@ -1,8 +1,9 @@ scoreritemResponseitemessayRubricconceptitemOutcome getItem() itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) + diff --git a/e2etests/testdata/stable/sequence_diagram_span/elk/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagram_span/elk/sketch.exp.svg index aa3321ec2..39b8a5c94 100644 --- a/e2etests/testdata/stable/sequence_diagram_span/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagram_span/elk/sketch.exp.svg @@ -1,8 +1,9 @@ scoreritemResponseitemessayRubricconceptitemOutcome getItem() itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) + diff --git a/e2etests/testdata/stable/sequence_diagrams/dagre/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagrams/dagre/sketch.exp.svg index db69a01ce..6d0144b14 100644 --- a/e2etests/testdata/stable/sequence_diagrams/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagrams/dagre/sketch.exp.svg @@ -1,8 +1,9 @@ a_shapea_sequenceanotherfinallysequencesequencesequencescoreritemResponseitemessayRubricconceptitemOutcomescorerconceptessayRubricitemitemOutcomeitemResponsescoreritemResponseitemessayRubricconceptitemOutcome getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts)getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) + diff --git a/e2etests/testdata/stable/sequence_diagrams/elk/sketch.exp.svg b/e2etests/testdata/stable/sequence_diagrams/elk/sketch.exp.svg index 7d5445201..6e41bc036 100644 --- a/e2etests/testdata/stable/sequence_diagrams/elk/sketch.exp.svg +++ b/e2etests/testdata/stable/sequence_diagrams/elk/sketch.exp.svg @@ -1,8 +1,9 @@ a_shapea_sequenceanotherfinallysequencesequencesequencescoreritemResponseitemessayRubricconceptitemOutcomescorerconceptessayRubricitemitemOutcomeitemResponsescoreritemResponseitemessayRubricconceptitemOutcome getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts)getItem()itemgetRubric()rubricapplyTo(essayResp)match(essayResponse)scorenewgetNormalMinimum()getNormalMaximum()setScore(score)setFeedback(missingConcepts) + diff --git a/e2etests/testdata/stable/sql_tables/dagre/sketch.exp.svg b/e2etests/testdata/stable/sql_tables/dagre/sketch.exp.svg index 8b78b0f66..025bd88a8 100644 --- a/e2etests/testdata/stable/sql_tables/dagre/sketch.exp.svg +++ b/e2etests/testdata/stable/sql_tables/dagre/sketch.exp.svg @@ -1,8 +1,9 @@ usersid int name string @@ -50,8 +71,8 @@ width="996" height="660" viewBox="-100 -100 996 660"> - + + usersid +usersid int name string @@ -50,8 +71,8 @@ width="916" height="660" viewBox="-88 -88 916 660"> - + + + rectangle -square - +square + + rectangle -square - +square + acbl1l2c1l2c3l2c2l3c1l3c2l4bacacbabcc1c2c3abc - +acbl1l2c1l2c3l2c2l3c1l3c2l4bacacbabcc1c2c3abc + acbl1l2c1l2c3l2c2l3c1l3c2l4bacacbabcc1c2c3abc - +acbl1l2c1l2c3l2c2l3c1l3c2l4bacacbabcc1c2c3abc + + @@ -28,8 +49,8 @@ width="314" height="552" viewBox="-100 -100 314 552"> x -y in style - +y in style + + @@ -28,8 +49,8 @@ width="314" height="673" viewBox="-88 -88 314 673"> x -y in style - +y in style + bearmama bearpapa bear + bearmama bearpapa bear + x +x @@ -45,8 +66,8 @@ width="314" height="552" viewBox="-100 -100 314 552"> Gee, I feel kind of LIGHT in the head now, -knowing I can't make my satellite dish PAYMENTS! - +knowing I can't make my satellite dish PAYMENTS! + x +x @@ -45,8 +66,8 @@ width="314" height="552" viewBox="-88 -88 314 552"> Gee, I feel kind of LIGHT in the head now, -knowing I can't make my satellite dish PAYMENTS! - +knowing I can't make my satellite dish PAYMENTS! + + -cube - +cube + + -cube - +cube + - +- num int- timeout @@ -42,8 +63,8 @@ width="622" height="2748" viewBox="-100 -100 622 2748">:= 5 := a + 7 -fmt.Printf("%d", b) - +fmt.Printf("%d", b) + - +- num int- timeout @@ -42,8 +63,8 @@ width="622" height="2748" viewBox="-88 -88 622 2748">:= 5 := a + 7 -fmt.Printf("%d", b) - +fmt.Printf("%d", b) + - +- num int- timeout @@ -42,8 +63,8 @@ width="712" height="1424" viewBox="-100 -100 712 1424">:= 5 := a + 7 -fmt.Printf("%d", b) - +fmt.Printf("%d", b) + - +- num int- timeout @@ -42,8 +63,8 @@ width="712" height="1424" viewBox="-88 -88 712 1424">:= 5 := a + 7 -fmt.Printf("%d", b) - +fmt.Printf("%d", b) + AKHIALFLGAMSTNAZCANVNMUTARLAMOOKTXORCOKSNEWYCTMANYRIDEMDNJPANCSCIDMTWAILINIAMIKYWIOHMNSDVAWVMENHVTND - +AKHIALFLGAMSTNAZCANVNMUTARLAMOOKTXORCOKSNEWYCTMANYRIDEMDNJPANCSCIDMTWAILINIAMIKYWIOHMNSDVAWVMENHVTND + AKHIALFLGAMSTNAZCANVNMUTARLAMOOKTXORCOKSNEWYCTMANYRIDEMDNJPANCSCIDMTWAILINIAMIKYWIOHMNSDVAWVMENHVTND - +AKHIALFLGAMSTNAZCANVNMUTARLAMOOKTXORCOKSNEWYCTMANYRIDEMDNJPANCSCIDMTWAILINIAMIKYWIOHMNSDVAWVMENHVTND + containerfirstsecond 1->2c->2 - +containerfirstsecond 1->2c->2 + containerfirstsecond 1->2c->2 - +containerfirstsecond 1->2c->2 + ninety ninesixty fourthirty twosixteeneight - +ninety ninesixty fourthirty twosixteeneight + ninety ninesixty fourthirty twosixteeneight - +ninety ninesixty fourthirty twosixteeneight + eightsixteenthirty twosixty fourninety nine twelvetwenty fourforty eighteighty one - +eightsixteenthirty twosixty fourninety nine twelvetwenty fourforty eighteighty one + diff --git a/e2etests/testdata/todo/font_sizes_large/elk/sketch.exp.svg b/e2etests/testdata/todo/font_sizes_large/elk/sketch.exp.svg index f1d7952c3..8689c9d30 100644 --- a/e2etests/testdata/todo/font_sizes_large/elk/sketch.exp.svg +++ b/e2etests/testdata/todo/font_sizes_large/elk/sketch.exp.svg @@ -1,8 +1,9 @@ eightsixteenthirty twosixty fourninety nine twelvetwenty fourforty eighteighty one + diff --git a/e2etests/testdata/todo/sequence_diagram_actor_padding_nested_groups/dagre/sketch.exp.svg b/e2etests/testdata/todo/sequence_diagram_actor_padding_nested_groups/dagre/sketch.exp.svg index 5bcd47f78..cacabd774 100644 --- a/e2etests/testdata/todo/sequence_diagram_actor_padding_nested_groups/dagre/sketch.exp.svg +++ b/e2etests/testdata/todo/sequence_diagram_actor_padding_nested_groups/dagre/sketch.exp.svg @@ -1,8 +1,9 @@ bacthis is a message groupand this is a nested message groupwhat about more nestingyoyo + diff --git a/e2etests/testdata/todo/sequence_diagram_actor_padding_nested_groups/elk/sketch.exp.svg b/e2etests/testdata/todo/sequence_diagram_actor_padding_nested_groups/elk/sketch.exp.svg index 5bcd47f78..cacabd774 100644 --- a/e2etests/testdata/todo/sequence_diagram_actor_padding_nested_groups/elk/sketch.exp.svg +++ b/e2etests/testdata/todo/sequence_diagram_actor_padding_nested_groups/elk/sketch.exp.svg @@ -1,8 +1,9 @@ bacthis is a message groupand this is a nested message groupwhat about more nestingyoyo + diff --git a/e2etests/testdata/todo/shape_set_width_height/dagre/sketch.exp.svg b/e2etests/testdata/todo/shape_set_width_height/dagre/sketch.exp.svg index c63fc847c..369de844c 100644 --- a/e2etests/testdata/todo/shape_set_width_height/dagre/sketch.exp.svg +++ b/e2etests/testdata/todo/shape_set_width_height/dagre/sketch.exp.svg @@ -1,8 +1,9 @@ containerscloudtall cylinderusersid int name string @@ -44,8 +65,8 @@ width="2478" height="2668" viewBox="-100 -100 2478 2668">:= 5 := a + 7 -fmt.Printf("%d", b)containercircle containerdiamond containeroval containerhexagon containerdiamondcirclehexagonoval - +fmt.Printf("%d", b)containercircle containerdiamond containeroval containerhexagon containerdiamondcirclehexagonoval + containerscloudtall cylinderusersid +containerscloudtall cylinderusersid int name string @@ -44,8 +65,8 @@ width="2618" height="2640" viewBox="-88 -88 2618 2640">:= 5 := a + 7 -fmt.Printf("%d", b)containercircle containerdiamond containeroval containerhexagon containerdiamondcirclehexagonoval - +fmt.Printf("%d", b)containercircle containerdiamond containeroval containerhexagon containerdiamondcirclehexagonoval + ab Thereoncewasaverytalledgelabel - +ab Thereoncewasaverytalledgelabel + ab Thereoncewasaverytalledgelabel - +ab Thereoncewasaverytalledgelabel +