diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md
index 32734248a..a71bf8156 100644
--- a/ci/release/changelogs/next.md
+++ b/ci/release/changelogs/next.md
@@ -1,9 +1,14 @@
#### Features ๐
+- `--center` flag centers the SVG in the containing viewbox. [#1056](https://github.com/terrastruct/d2/pull/1056)
+
#### Improvements ๐งน
- `--browser` flag on CLI controls `BROWSER` environment variable for not opening browser in watch mode. [#1052](https://github.com/terrastruct/d2/pull/1052)
+- `elk` layout containers no longer overlap the label with children. [#1055](https://github.com/terrastruct/d2/pull/1055)
+- `
` attribute of HTML in watch mode is the base file name, instead of the whole path. [#1054](https://github.com/terrastruct/d2/pull/1054)
#### Bugfixes โ๏ธ
+- Code blocks are not affected by uppercasing from special themes like Terminal. [#1053](https://github.com/terrastruct/d2/pull/1053)
- Fixes fill-pattern replacement in the API. [#1051](https://github.com/terrastruct/d2/pull/1051)
diff --git a/ci/release/template/man/d2.1 b/ci/release/template/man/d2.1
index 0981c0226..6dce8cf36 100644
--- a/ci/release/template/man/d2.1
+++ b/ci/release/template/man/d2.1
@@ -77,6 +77,9 @@ making style maps in D2 light/dark mode specific. See
.It Fl s , -sketch Ar false
Renders the diagram to look like it was sketched by hand
.Ns .
+.It Fl -center Ar flag
+Center the SVG in the containing viewbox, such as your browser screen
+.Ns .
.It Fl -pad Ar 100
Pixels padded around the rendered diagram
.Ns .
diff --git a/d2cli/main.go b/d2cli/main.go
index 0c5f83fac..fbf690930 100644
--- a/d2cli/main.go
+++ b/d2cli/main.go
@@ -84,6 +84,10 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
return err
}
browserFlag := ms.Opts.String("BROWSER", "browser", "", "", "browser executable that watch opens. Setting to 0 opens no browser.")
+ centerFlag, err := ms.Opts.Bool("D2_CENTER", "center", "c", false, "center the SVG in the containing viewbox, such as your browser screen")
+ if err != nil {
+ return err
+ }
ps, err := d2plugin.ListPlugins(ctx)
if err != nil {
@@ -233,6 +237,7 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
w, err := newWatcher(ctx, ms, watcherOpts{
layoutPlugin: plugin,
sketch: *sketchFlag,
+ center: *centerFlag,
themeID: *themeFlag,
darkThemeID: darkThemeFlag,
pad: *padFlag,
@@ -253,7 +258,7 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
ctx, cancel := context.WithTimeout(ctx, time.Minute*2)
defer cancel()
- _, written, err := compile(ctx, ms, plugin, *sketchFlag, *padFlag, *themeFlag, darkThemeFlag, inputPath, outputPath, *bundleFlag, *forceAppendixFlag, pw.Page)
+ _, written, err := compile(ctx, ms, plugin, *sketchFlag, *centerFlag, *padFlag, *themeFlag, darkThemeFlag, inputPath, outputPath, *bundleFlag, *forceAppendixFlag, pw.Page)
if err != nil {
if written {
return fmt.Errorf("failed to fully compile (partial render written): %w", err)
@@ -263,7 +268,7 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
return nil
}
-func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, sketch bool, pad, themeID int64, darkThemeID *int64, inputPath, outputPath string, bundle, forceAppendix bool, page playwright.Page) (_ []byte, written bool, _ error) {
+func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, sketch, center bool, pad, themeID int64, darkThemeID *int64, inputPath, outputPath string, bundle, forceAppendix bool, page playwright.Page) (_ []byte, written bool, _ error) {
start := time.Now()
input, err := ms.ReadPath(inputPath)
if err != nil {
@@ -302,10 +307,10 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, sketc
var svg []byte
if filepath.Ext(outputPath) == ".pdf" {
pageMap := pdf.BuildPDFPageMap(diagram, nil, nil)
- svg, err = renderPDF(ctx, ms, plugin, sketch, pad, themeID, outputPath, page, ruler, diagram, nil, nil, pageMap)
+ svg, err = renderPDF(ctx, ms, plugin, sketch, center, pad, themeID, outputPath, page, ruler, diagram, nil, nil, pageMap)
} else {
compileDur := time.Since(start)
- svg, err = render(ctx, ms, compileDur, plugin, sketch, pad, themeID, darkThemeID, inputPath, outputPath, bundle, forceAppendix, page, ruler, diagram)
+ svg, err = render(ctx, ms, compileDur, plugin, sketch, center, pad, themeID, darkThemeID, inputPath, outputPath, bundle, forceAppendix, page, ruler, diagram)
}
if err != nil {
return svg, false, err
@@ -319,7 +324,7 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, sketc
return svg, true, nil
}
-func render(ctx context.Context, ms *xmain.State, compileDur time.Duration, plugin d2plugin.Plugin, sketch bool, pad int64, themeID int64, darkThemeID *int64, inputPath, outputPath string, bundle, forceAppendix bool, page playwright.Page, ruler *textmeasure.Ruler, diagram *d2target.Diagram) ([]byte, error) {
+func render(ctx context.Context, ms *xmain.State, compileDur time.Duration, plugin d2plugin.Plugin, sketch, center bool, pad int64, themeID int64, darkThemeID *int64, inputPath, outputPath string, bundle, forceAppendix bool, page playwright.Page, ruler *textmeasure.Ruler, diagram *d2target.Diagram) ([]byte, error) {
if diagram.Name != "" {
ext := filepath.Ext(outputPath)
outputPath = strings.TrimSuffix(outputPath, ext)
@@ -363,19 +368,19 @@ func render(ctx context.Context, ms *xmain.State, compileDur time.Duration, plug
}
for _, dl := range diagram.Layers {
- _, err := render(ctx, ms, compileDur, plugin, sketch, pad, themeID, darkThemeID, inputPath, layersOutputPath, bundle, forceAppendix, page, ruler, dl)
+ _, err := render(ctx, ms, compileDur, plugin, sketch, center, pad, themeID, darkThemeID, inputPath, layersOutputPath, bundle, forceAppendix, page, ruler, dl)
if err != nil {
return nil, err
}
}
for _, dl := range diagram.Scenarios {
- _, err := render(ctx, ms, compileDur, plugin, sketch, pad, themeID, darkThemeID, inputPath, scenariosOutputPath, bundle, forceAppendix, page, ruler, dl)
+ _, err := render(ctx, ms, compileDur, plugin, sketch, center, pad, themeID, darkThemeID, inputPath, scenariosOutputPath, bundle, forceAppendix, page, ruler, dl)
if err != nil {
return nil, err
}
}
for _, dl := range diagram.Steps {
- _, err := render(ctx, ms, compileDur, plugin, sketch, pad, themeID, darkThemeID, inputPath, stepsOutputPath, bundle, forceAppendix, page, ruler, dl)
+ _, err := render(ctx, ms, compileDur, plugin, sketch, center, pad, themeID, darkThemeID, inputPath, stepsOutputPath, bundle, forceAppendix, page, ruler, dl)
if err != nil {
return nil, err
}
@@ -383,7 +388,7 @@ func render(ctx context.Context, ms *xmain.State, compileDur time.Duration, plug
if !diagram.IsFolderOnly {
start := time.Now()
- svg, err := _render(ctx, ms, plugin, sketch, pad, themeID, darkThemeID, boardOutputPath, bundle, forceAppendix, page, ruler, diagram)
+ svg, err := _render(ctx, ms, plugin, sketch, center, pad, themeID, darkThemeID, boardOutputPath, bundle, forceAppendix, page, ruler, diagram)
if err != nil {
return svg, err
}
@@ -395,11 +400,12 @@ func render(ctx context.Context, ms *xmain.State, compileDur time.Duration, plug
return nil, nil
}
-func _render(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, sketch bool, pad int64, themeID int64, darkThemeID *int64, outputPath string, bundle, forceAppendix bool, page playwright.Page, ruler *textmeasure.Ruler, diagram *d2target.Diagram) ([]byte, error) {
+func _render(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, sketch, center bool, pad int64, themeID int64, darkThemeID *int64, outputPath string, bundle, forceAppendix bool, page playwright.Page, ruler *textmeasure.Ruler, diagram *d2target.Diagram) ([]byte, error) {
toPNG := filepath.Ext(outputPath) == ".png"
svg, err := d2svg.Render(diagram, &d2svg.RenderOpts{
Pad: int(pad),
Sketch: sketch,
+ Center: center,
ThemeID: themeID,
DarkThemeID: darkThemeID,
SetDimensions: toPNG,
@@ -461,7 +467,7 @@ func _render(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, sketc
return svg, nil
}
-func renderPDF(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, sketch bool, pad, themeID int64, outputPath string, page playwright.Page, ruler *textmeasure.Ruler, diagram *d2target.Diagram, pdf *pdflib.GoFPDF, boardPath []string, pageMap map[string]int) (svg []byte, err error) {
+func renderPDF(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, sketch, center bool, pad, themeID int64, outputPath string, page playwright.Page, ruler *textmeasure.Ruler, diagram *d2target.Diagram, pdf *pdflib.GoFPDF, boardPath []string, pageMap map[string]int) (svg []byte, err error) {
var isRoot bool
if pdf == nil {
pdf = pdflib.Init()
@@ -489,6 +495,7 @@ func renderPDF(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, ske
svg, err = d2svg.Render(diagram, &d2svg.RenderOpts{
Pad: int(pad),
Sketch: sketch,
+ Center: center,
SetDimensions: true,
})
if err != nil {
@@ -529,19 +536,19 @@ func renderPDF(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, ske
}
for _, dl := range diagram.Layers {
- _, err := renderPDF(ctx, ms, plugin, sketch, pad, themeID, "", page, ruler, dl, pdf, currBoardPath, pageMap)
+ _, err := renderPDF(ctx, ms, plugin, sketch, center, pad, themeID, "", page, ruler, dl, pdf, currBoardPath, pageMap)
if err != nil {
return nil, err
}
}
for _, dl := range diagram.Scenarios {
- _, err := renderPDF(ctx, ms, plugin, sketch, pad, themeID, "", page, ruler, dl, pdf, currBoardPath, pageMap)
+ _, err := renderPDF(ctx, ms, plugin, sketch, center, pad, themeID, "", page, ruler, dl, pdf, currBoardPath, pageMap)
if err != nil {
return nil, err
}
}
for _, dl := range diagram.Steps {
- _, err := renderPDF(ctx, ms, plugin, sketch, pad, themeID, "", page, ruler, dl, pdf, currBoardPath, pageMap)
+ _, err := renderPDF(ctx, ms, plugin, sketch, center, pad, themeID, "", page, ruler, dl, pdf, currBoardPath, pageMap)
if err != nil {
return nil, err
}
diff --git a/d2cli/watch.go b/d2cli/watch.go
index ca2280dc0..f9cfb9e81 100644
--- a/d2cli/watch.go
+++ b/d2cli/watch.go
@@ -44,6 +44,7 @@ type watcherOpts struct {
darkThemeID *int64
pad int64
sketch bool
+ center bool
host string
port string
inputPath string
@@ -359,7 +360,7 @@ func (w *watcher) compileLoop(ctx context.Context) error {
w.pw = newPW
}
- svg, _, err := compile(ctx, w.ms, w.layoutPlugin, w.sketch, w.pad, w.themeID, w.darkThemeID, w.inputPath, w.outputPath, w.bundle, w.forceAppendix, w.pw.Page)
+ svg, _, err := compile(ctx, w.ms, w.layoutPlugin, w.sketch, w.center, w.pad, w.themeID, w.darkThemeID, w.inputPath, w.outputPath, w.bundle, w.forceAppendix, w.pw.Page)
errs := ""
if err != nil {
if len(svg) > 0 {
@@ -433,7 +434,7 @@ func (w *watcher) handleRoot(hw http.ResponseWriter, r *http.Request) {