From a972b5b0ee065541486baf5b601b9413c27bef0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojt=C4=9Bch=20Fo=C5=A1n=C3=A1r?= Date: Mon, 9 Jan 2023 22:16:22 +0100 Subject: [PATCH] removed --sketch_bg and added --dark_theme option --- d2renderers/d2svg/d2svg.go | 67 +++++- d2renderers/d2svg/style.css | 450 ------------------------------------ main.go | 34 ++- watch.go | 4 +- 4 files changed, 81 insertions(+), 474 deletions(-) diff --git a/d2renderers/d2svg/d2svg.go b/d2renderers/d2svg/d2svg.go index a6c2afea2..cd0f299a3 100644 --- a/d2renderers/d2svg/d2svg.go +++ b/d2renderers/d2svg/d2svg.go @@ -27,6 +27,7 @@ import ( "oss.terrastruct.com/d2/d2renderers/d2latex" "oss.terrastruct.com/d2/d2renderers/d2sketch" "oss.terrastruct.com/d2/d2target" + "oss.terrastruct.com/d2/d2themes/d2themescatalog" "oss.terrastruct.com/d2/lib/color" "oss.terrastruct.com/d2/lib/geo" "oss.terrastruct.com/d2/lib/label" @@ -53,7 +54,7 @@ var TooltipIcon string var LinkIcon string //go:embed style.css -var styleCSS string +var baseStylesheet string //go:embed sketchstyle.css var sketchStyleCSS string @@ -62,10 +63,10 @@ var sketchStyleCSS string var mdCSS string type RenderOpts struct { - Pad int - Sketch bool - SketchBg bool - ThemeID int64 + Pad int + Sketch bool + ThemeID int64 + DarkThemeID int64 } func dimensions(writer io.Writer, diagram *d2target.Diagram, pad int) (width, height int, topLeft, bottomRight d2target.Point) { @@ -1148,16 +1149,19 @@ var fitToScreenScript string const ( BG_COLOR = color.N7 FG_COLOR = color.N1 + + DEFAULT_THEME int64 = 0 + DEFAULT_DARK_THEME int64 = math.MaxInt64 // no theme selected ) // TODO minify output at end func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) { var sketchRunner *d2sketch.Runner pad := DEFAULT_PADDING - sketchBg := true + themeID := DEFAULT_THEME + darkThemeID := DEFAULT_DARK_THEME if opts != nil { pad = opts.Pad - sketchBg = opts.SketchBg if opts.Sketch { var err error sketchRunner, err = d2sketch.InitSketchVM() @@ -1165,6 +1169,8 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) { return nil, err } } + themeID = opts.ThemeID + darkThemeID = opts.DarkThemeID } buf := &bytes.Buffer{} @@ -1245,11 +1251,12 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) { // containerEl.Color = color.N1 TODO this is useless as this element has no children // generate elements that will be appended to the SVG tag - styleCSS2 := "" + themeStylesheet := themeCSS(themeID, darkThemeID) + sketchStylesheet := "" if sketchRunner != nil { - styleCSS2 = "\n" + sketchStyleCSS + sketchStylesheet = "\n" + sketchStyleCSS } - svgOut := fmt.Sprintf(``, styleCSS, styleCSS2) + svgOut := fmt.Sprintf(``, baseStylesheet, themeStylesheet, sketchStylesheet) // 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 svgOut += fmt.Sprintf(``, fitToScreenScript) @@ -1263,7 +1270,7 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) { if hasMarkdown { svgOut += fmt.Sprintf(``, mdCSS) } - if sketchRunner != nil && sketchBg { + if sketchRunner != nil { svgOut += d2sketch.DefineFillPattern() } svgOut += embedFonts(buf, diagram.FontFamily) @@ -1278,6 +1285,44 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) { return []byte(docRendered), nil } +func themeCSS(themeID, darkThemeID int64) (stylesheet string) { + out := singleThemeRulesets(themeID) + + if darkThemeID != math.MaxInt64 { + out += fmt.Sprintf("@media screen and (prefers-color-scheme:dark){%s}", singleThemeRulesets(darkThemeID)) + } + + return out +} + +func singleThemeRulesets(themeID int64) (rulesets string) { + out := "" + theme := d2themescatalog.Find(themeID) + for _, property := range []string{"fill", "stroke", "background-color", "color"} { + out += fmt.Sprintf(".%s-N1{%s:%s;}.%s-N2{%s:%s;}.%s-N3{%s:%s;}.%s-N4{%s:%s;}.%s-N5{%s:%s;}.%s-N6{%s:%s;}.%s-N7{%s:%s;}.%s-B1{%s:%s;}.%s-B2{%s:%s;}.%s-B3{%s:%s;}.%s-B4{%s:%s;}.%s-B5{%s:%s;}.%s-B6{%s:%s;}.%s-AA2{%s:%s;}.%s-AA4{%s:%s;}.%s-AA5{%s:%s;}.%s-AB4{%s:%s;}.%s-AB5{%s:%s;}", + property, property, theme.Colors.Neutrals.N1, + property, property, theme.Colors.Neutrals.N2, + property, property, theme.Colors.Neutrals.N3, + property, property, theme.Colors.Neutrals.N4, + property, property, theme.Colors.Neutrals.N5, + property, property, theme.Colors.Neutrals.N6, + property, property, theme.Colors.Neutrals.N7, + property, property, theme.Colors.B1, + property, property, theme.Colors.B2, + property, property, theme.Colors.B3, + property, property, theme.Colors.B4, + property, property, theme.Colors.B5, + property, property, theme.Colors.B6, + property, property, theme.Colors.AA2, + property, property, theme.Colors.AA4, + property, property, theme.Colors.AA5, + property, property, theme.Colors.AB4, + property, property, theme.Colors.AB5, + ) + } + return out +} + type DiagramObject interface { GetID() string GetZIndex() int diff --git a/d2renderers/d2svg/style.css b/d2renderers/d2svg/style.css index a5a8e6c39..7312ba8de 100644 --- a/d2renderers/d2svg/style.css +++ b/d2renderers/d2svg/style.css @@ -10,453 +10,3 @@ mix-Blend-mode: multiply; opacity: 0.5; } - -/* -.fill -.stroke - -.background-color -.color -*/ - -.fill-N1 { - fill: #0A0F25; -} -.fill-N2 { - fill: #676C7E; -} -.fill-N3 { - fill: #9499AB; -} -.fill-N4 { - fill: #CFD2DD; -} -.fill-N5 { - fill: #DEE1EB; -} -.fill-N6 { - fill: #EEF1F8; -} -.fill-N7 { - fill: #FFFFFF; -} -.fill-B1 { - fill: #0D32B2; -} -.fill-B2 { - fill: #0D32B2; -} -.fill-B3 { - fill: #E3E9FD; -} -.fill-B4 { - fill: #E3E9FD; -} -.fill-B5 { - fill: #EDF0FD; -} -.fill-B6 { - fill: #F7F8FE; -} -.fill-AA2 { - fill: #4A6FF3; -} -.fill-AA4 { - fill: #EDF0FD; -} -.fill-AA5 { - fill: #F7F8FE; -} -.fill-AB4 { - fill: #DEE1EB; -} -.fill-AB5 { - fill: #F7F8FE; -} - -.stroke-N1 { - stroke: #0A0F25; -} -.stroke-N2 { - stroke: #676C7E; -} -.stroke-N3 { - stroke: #9499AB; -} -.stroke-N4 { - stroke: #CFD2DD; -} -.stroke-N5 { - stroke: #DEE1EB; -} -.stroke-N6 { - stroke: #EEF1F8; -} -.stroke-N7 { - stroke: #FFFFFF; -} -.stroke-B1 { - stroke: #0D32B2; -} -.stroke-B2 { - stroke: #0D32B2; -} -.stroke-B3 { - stroke: #E3E9FD; -} -.stroke-B4 { - stroke: #E3E9FD; -} -.stroke-B5 { - stroke: #EDF0FD; -} -.stroke-B6 { - stroke: #F7F8FE; -} -.stroke-AA2 { - stroke: #4A6FF3; -} -.stroke-AA4 { - stroke: #EDF0FD; -} -.stroke-AA5 { - stroke: #F7F8FE; -} -.stroke-AB4 { - stroke: #DEE1EB; -} -.stroke-AB5 { - stroke: #F7F8FE; -} - -.background-color-N1 { - background-color: #0A0F25; -} -.background-color-N2 { - background-color: #676C7E; -} -.background-color-N3 { - background-color: #9499AB; -} -.background-color-N4 { - background-color: #CFD2DD; -} -.background-color-N5 { - background-color: #DEE1EB; -} -.background-color-N6 { - background-color: #EEF1F8; -} -.background-color-N7 { - background-color: #FFFFFF; -} -.background-color-B1 { - background-color: #0D32B2; -} -.background-color-B2 { - background-color: #0D32B2; -} -.background-color-B3 { - background-color: #E3E9FD; -} -.background-color-B4 { - background-color: #E3E9FD; -} -.background-color-B5 { - background-color: #EDF0FD; -} -.background-color-B6 { - background-color: #F7F8FE; -} -.background-color-AA2 { - background-color: #4A6FF3; -} -.background-color-AA4 { - background-color: #EDF0FD; -} -.background-color-AA5 { - background-color: #F7F8FE; -} -.background-color-AB4 { - background-color: #DEE1EB; -} -.background-color-AB5 { - background-color: #F7F8FE; -} - -.color-N1 { - color: #0A0F25; -} -.color-N2 { - color: #676C7E; -} -.color-N3 { - color: #9499AB; -} -.color-N4 { - color: #CFD2DD; -} -.color-N5 { - color: #DEE1EB; -} -.color-N6 { - color: #EEF1F8; -} -.color-N7 { - color: #FFFFFF; -} -.color-B1 { - color: #0D32B2; -} -.color-B2 { - color: #0D32B2; -} -.color-B3 { - color: #E3E9FD; -} -.color-B4 { - color: #E3E9FD; -} -.color-B5 { - color: #EDF0FD; -} -.color-B6 { - color: #F7F8FE; -} -.color-AA2 { - color: #4A6FF3; -} -.color-AA4 { - color: #EDF0FD; -} -.color-AA5 { - color: #F7F8FE; -} -.color-AB4 { - color: #DEE1EB; -} -.color-AB5 { - color: #F7F8FE; -} - -@media screen and (prefers-color-scheme: dark) { - .fill-N1 { - fill: #cdd6f4; - } - .fill-N2 { - fill: #bac2de; - } - .fill-N3 { - fill: #a6adc8; - } - .fill-N4 { - fill: #585b70; - } - .fill-N5 { - fill: #45475a; - } - .fill-N6 { - fill: #313244; - } - .fill-N7 { - fill: #1e1e2e; - } - .fill-B1 { - fill: #cba6f7; - } - .fill-B2 { - fill: #cba6f7; - } - .fill-B3 { - fill: #6c7086; - } - .fill-B4 { - fill: #585b70; - } - .fill-B5 { - fill: #45475a; - } - .fill-B6 { - fill: #313244; - } - .fill-AA2 { - fill: #f38ba8; - } - .fill-AA4 { - fill: #45475a; - } - .fill-AA5 { - fill: #313244; - } - .fill-AB4 { - fill: #45475a; - } - .fill-AB5 { - fill: #313244; - } - - .stroke-N1 { - stroke: #cdd6f4; - } - .stroke-N2 { - stroke: #bac2de; - } - .stroke-N3 { - stroke: #a6adc8; - } - .stroke-N4 { - stroke: #585b70; - } - .stroke-N5 { - stroke: #45475a; - } - .stroke-N6 { - stroke: #313244; - } - .stroke-N7 { - stroke: #1e1e2e; - } - .stroke-B1 { - stroke: #cba6f7; - } - .stroke-B2 { - stroke: #cba6f7; - } - .stroke-B3 { - stroke: #6c7086; - } - .stroke-B4 { - stroke: #585b70; - } - .stroke-B5 { - stroke: #45475a; - } - .stroke-B6 { - stroke: #313244; - } - .stroke-AA2 { - stroke: #f38ba8; - } - .stroke-AA4 { - stroke: #45475a; - } - .stroke-AA5 { - stroke: #313244; - } - .stroke-AB4 { - stroke: #45475a; - } - .stroke-AB5 { - stroke: #313244; - } - - .background-color-N1 { - background-color: #cdd6f4; - } - .background-color-N2 { - background-color: #bac2de; - } - .background-color-N3 { - background-color: #a6adc8; - } - .background-color-N4 { - background-color: #585b70; - } - .background-color-N5 { - background-color: #45475a; - } - .background-color-N6 { - background-color: #313244; - } - .background-color-N7 { - background-color: #1e1e2e; - } - .background-color-B1 { - background-color: #cba6f7; - } - .background-color-B2 { - background-color: #cba6f7; - } - .background-color-B3 { - background-color: #6c7086; - } - .background-color-B4 { - background-color: #585b70; - } - .background-color-B5 { - background-color: #45475a; - } - .background-color-B6 { - background-color: #313244; - } - .background-color-AA2 { - background-color: #f38ba8; - } - .background-color-AA4 { - background-color: #45475a; - } - .background-color-AA5 { - background-color: #313244; - } - .background-color-AB4 { - background-color: #45475a; - } - .background-color-AB5 { - background-color: #313244; - } - - .color-N1 { - color: #cdd6f4; - } - .color-N2 { - color: #bac2de; - } - .color-N3 { - color: #a6adc8; - } - .color-N4 { - color: #585b70; - } - .color-N5 { - color: #45475a; - } - .color-N6 { - color: #313244; - } - .color-N7 { - color: #1e1e2e; - } - .color-B1 { - color: #cba6f7; - } - .color-B2 { - color: #cba6f7; - } - .color-B3 { - color: #6c7086; - } - .color-B4 { - color: #585b70; - } - .color-B5 { - color: #45475a; - } - .color-B6 { - color: #313244; - } - .color-AA2 { - color: #f38ba8; - } - .color-AA4 { - color: #45475a; - } - .color-AA5 { - color: #313244; - } - .color-AB4 { - color: #45475a; - } - .color-AB5 { - color: #313244; - } -} diff --git a/main.go b/main.go index 948c59357..5afbbaf67 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "io" + "math" "os/exec" "path/filepath" "strings" @@ -62,6 +63,10 @@ func run(ctx context.Context, ms *xmain.State) (err error) { if err != nil { return err } + darkThemeFlag, err := ms.Opts.Int64("D2_D_THEME", "dark_theme", "", math.MaxInt64, "the diagram dark theme ID. When left unset only the theme will be applied") + if err != nil { + return err + } padFlag, err := ms.Opts.Int64("D2_PAD", "pad", "", d2svg.DEFAULT_PADDING, "pixels padded around the rendered diagram") if err != nil { return err @@ -75,11 +80,6 @@ func run(ctx context.Context, ms *xmain.State) (err error) { return err } - sketchBgFlag, err := ms.Opts.Bool("D2_SKT_BG", "sketch_bg", "", true, "make the background look like it was sketched too") - if err != nil { - return err - } - ps, err := d2plugin.ListPlugins(ctx) if err != nil { return err @@ -151,6 +151,14 @@ func run(ctx context.Context, ms *xmain.State) (err error) { } ms.Log.Debug.Printf("using theme %s (ID: %d)", match.Name, *themeFlag) + if *darkThemeFlag != math.MaxInt64 { + match = d2themescatalog.Find(*darkThemeFlag) + if match == (d2themes.Theme{}) { + return xmain.UsageErrorf("--dark_theme could not be found. The available options are:\n%s\nYou provided: %d", d2themescatalog.CLIString(), *darkThemeFlag) + } + ms.Log.Debug.Printf("using dark theme %s (ID: %d)", match.Name, *darkThemeFlag) + } + plugin, err := d2plugin.FindPlugin(ctx, ps, *layoutFlag) if err != nil { if errors.Is(err, exec.ErrNotFound) { @@ -176,6 +184,9 @@ func run(ctx context.Context, ms *xmain.State) (err error) { var pw png.Playwright if filepath.Ext(outputPath) == ".png" { + if *darkThemeFlag != math.MaxInt64 { + return xmain.UsageErrorf("--dark_theme cannot be used while exporting to another format other than .svg") + } pw, err = png.InitPlaywright() if err != nil { return err @@ -197,6 +208,7 @@ func run(ctx context.Context, ms *xmain.State) (err error) { layoutPlugin: plugin, sketch: *sketchFlag, themeID: *themeFlag, + darkThemeID: *darkThemeFlag, pad: *padFlag, host: *hostFlag, port: *portFlag, @@ -214,7 +226,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, *sketchBgFlag, *padFlag, *themeFlag, inputPath, outputPath, *bundleFlag, pw.Page) + _, written, err := compile(ctx, ms, plugin, *sketchFlag, *padFlag, *themeFlag, *darkThemeFlag, inputPath, outputPath, *bundleFlag, pw.Page) if err != nil { if written { return fmt.Errorf("failed to fully compile (partial render written): %w", err) @@ -225,7 +237,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, sketchBg bool, pad, themeID int64, inputPath, outputPath string, bundle bool, page playwright.Page) (_ []byte, written bool, _ error) { +func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, sketch bool, pad, themeID, themeDarkID int64, inputPath, outputPath string, bundle bool, page playwright.Page) (_ []byte, written bool, _ error) { input, err := ms.ReadPath(inputPath) if err != nil { return nil, false, err @@ -250,10 +262,10 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, sketc } svg, err := d2svg.Render(diagram, &d2svg.RenderOpts{ - Pad: int(pad), - Sketch: sketch, - SketchBg: sketchBg, - ThemeID: themeID, + Pad: int(pad), + Sketch: sketch, + ThemeID: themeID, + DarkThemeID: themeDarkID, }) if err != nil { return nil, false, err diff --git a/watch.go b/watch.go index 530ad7d70..d98a447f0 100644 --- a/watch.go +++ b/watch.go @@ -41,9 +41,9 @@ var staticFS embed.FS type watcherOpts struct { layoutPlugin d2plugin.Plugin themeID int64 + darkThemeID int64 pad int64 sketch bool - sketchBg bool host string port string inputPath string @@ -357,7 +357,7 @@ func (w *watcher) compileLoop(ctx context.Context) error { w.pw = newPW } - svg, _, err := compile(ctx, w.ms, w.layoutPlugin, w.sketch, w.sketchBg, w.pad, w.themeID, w.inputPath, w.outputPath, w.bundle, w.pw.Page) + svg, _, err := compile(ctx, w.ms, w.layoutPlugin, w.sketch, w.pad, w.themeID, w.darkThemeID, w.inputPath, w.outputPath, w.bundle, w.pw.Page) errs := "" if err != nil { if len(svg) > 0 {