config vars
This commit is contained in:
parent
6b65f298b2
commit
45b396c894
31 changed files with 2015 additions and 229 deletions
|
|
@ -102,7 +102,7 @@ func test(t *testing.T, textPath, text string) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
g, err := d2compiler.Compile("", strings.NewReader(text), nil)
|
||||
g, _, err := d2compiler.Compile("", strings.NewReader(text), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
126
d2cli/main.go
126
d2cli/main.go
|
|
@ -20,6 +20,7 @@ import (
|
|||
"oss.terrastruct.com/util-go/go2"
|
||||
"oss.terrastruct.com/util-go/xmain"
|
||||
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/d2lib"
|
||||
"oss.terrastruct.com/d2/d2parser"
|
||||
"oss.terrastruct.com/d2/d2plugin"
|
||||
|
|
@ -117,11 +118,11 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
fontBoldFlag := ms.Opts.String("D2_FONT_BOLD", "font-bold", "", "", "path to .ttf file to use for the bold font. If none provided, Source Sans Pro Bold is used.")
|
||||
fontSemiboldFlag := ms.Opts.String("D2_FONT_SEMIBOLD", "font-semibold", "", "", "path to .ttf file to use for the semibold font. If none provided, Source Sans Pro Semibold is used.")
|
||||
|
||||
ps, err := d2plugin.ListPlugins(ctx)
|
||||
plugins, err := d2plugin.ListPlugins(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = populateLayoutOpts(ctx, ms, ps)
|
||||
err = populateLayoutOpts(ctx, ms, plugins)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -146,7 +147,7 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
case "init-playwright":
|
||||
return initPlaywright()
|
||||
case "layout":
|
||||
return layoutCmd(ctx, ms, ps)
|
||||
return layoutCmd(ctx, ms, plugins)
|
||||
case "themes":
|
||||
themesCmd(ctx, ms)
|
||||
return nil
|
||||
|
|
@ -226,6 +227,38 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
}
|
||||
ms.Log.Debug.Printf("using theme %s (ID: %d)", match.Name, *themeFlag)
|
||||
|
||||
// If flag is not explicitly set by user, set to nil.
|
||||
// Later, configs from D2 code will only overwrite if they weren't explicitly set by user
|
||||
flagSet := make(map[string]struct{})
|
||||
ms.Opts.Flags.Visit(func(f *pflag.Flag) {
|
||||
flagSet[f.Name] = struct{}{}
|
||||
})
|
||||
if ms.Env.Getenv("D2_LAYOUT") == "" {
|
||||
if _, ok := flagSet["layout"]; !ok {
|
||||
layoutFlag = nil
|
||||
}
|
||||
}
|
||||
if ms.Env.Getenv("D2_THEME") == "" {
|
||||
if _, ok := flagSet["theme"]; !ok {
|
||||
themeFlag = nil
|
||||
}
|
||||
}
|
||||
if ms.Env.Getenv("D2_SKETCH") == "" {
|
||||
if _, ok := flagSet["sketch"]; !ok {
|
||||
sketchFlag = nil
|
||||
}
|
||||
}
|
||||
if ms.Env.Getenv("D2_PAD") == "" {
|
||||
if _, ok := flagSet["pad"]; !ok {
|
||||
padFlag = nil
|
||||
}
|
||||
}
|
||||
if ms.Env.Getenv("D2_CENTER") == "" {
|
||||
if _, ok := flagSet["center"]; !ok {
|
||||
centerFlag = nil
|
||||
}
|
||||
}
|
||||
|
||||
if *darkThemeFlag == -1 {
|
||||
darkThemeFlag = nil // TODO this is a temporary solution: https://github.com/terrastruct/util-go/issues/7
|
||||
}
|
||||
|
|
@ -241,29 +274,6 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
scale = scaleFlag
|
||||
}
|
||||
|
||||
plugin, err := d2plugin.FindPlugin(ctx, ps, *layoutFlag)
|
||||
if err != nil {
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
return layoutNotFound(ctx, ps, *layoutFlag)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
err = d2plugin.HydratePluginOpts(ctx, ms, plugin)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pinfo, err := plugin.Info(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
plocation := pinfo.Type
|
||||
if pinfo.Type == "binary" {
|
||||
plocation = fmt.Sprintf("executable plugin at %s", humanPath(pinfo.Path))
|
||||
}
|
||||
ms.Log.Debug.Printf("using layout plugin %s (%s)", *layoutFlag, plocation)
|
||||
|
||||
if !outputFormat.supportsDarkTheme() {
|
||||
if darkThemeFlag != nil {
|
||||
ms.Log.Warn.Printf("--dark-theme cannot be used while exporting to another format other than .svg")
|
||||
|
|
@ -285,10 +295,10 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
}
|
||||
|
||||
renderOpts := d2svg.RenderOpts{
|
||||
Pad: int(*padFlag),
|
||||
Sketch: *sketchFlag,
|
||||
Center: *centerFlag,
|
||||
ThemeID: *themeFlag,
|
||||
Pad: padFlag,
|
||||
Sketch: sketchFlag,
|
||||
Center: centerFlag,
|
||||
ThemeID: themeFlag,
|
||||
DarkThemeID: darkThemeFlag,
|
||||
Scale: scale,
|
||||
}
|
||||
|
|
@ -298,7 +308,8 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
return xmain.UsageErrorf("-w[atch] cannot be combined with reading input from stdin")
|
||||
}
|
||||
w, err := newWatcher(ctx, ms, watcherOpts{
|
||||
layoutPlugin: plugin,
|
||||
plugins: plugins,
|
||||
layout: layoutFlag,
|
||||
renderOpts: renderOpts,
|
||||
animateInterval: *animateIntervalFlag,
|
||||
host: *hostFlag,
|
||||
|
|
@ -319,7 +330,7 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
ctx, cancel := timelib.WithTimeout(ctx, time.Minute*2)
|
||||
defer cancel()
|
||||
|
||||
_, written, err := compile(ctx, ms, plugin, renderOpts, fontFamily, *animateIntervalFlag, inputPath, outputPath, *bundleFlag, *forceAppendixFlag, pw.Page)
|
||||
_, written, err := compile(ctx, ms, plugins, layoutFlag, renderOpts, fontFamily, *animateIntervalFlag, inputPath, outputPath, *bundleFlag, *forceAppendixFlag, pw.Page)
|
||||
if err != nil {
|
||||
if written {
|
||||
return fmt.Errorf("failed to fully compile (partial render written): %w", err)
|
||||
|
|
@ -329,7 +340,32 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
return nil
|
||||
}
|
||||
|
||||
func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, renderOpts d2svg.RenderOpts, fontFamily *d2fonts.FontFamily, animateInterval int64, inputPath, outputPath string, bundle, forceAppendix bool, page playwright.Page) (_ []byte, written bool, _ error) {
|
||||
func LayoutResolver(ctx context.Context, ms *xmain.State, plugins []d2plugin.Plugin) func(engine string) (d2graph.LayoutGraph, error) {
|
||||
cached := make(map[string]d2graph.LayoutGraph)
|
||||
return func(engine string) (d2graph.LayoutGraph, error) {
|
||||
if c, ok := cached[engine]; ok {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
plugin, err := d2plugin.FindPlugin(ctx, plugins, engine)
|
||||
if err != nil {
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
return nil, layoutNotFound(ctx, plugins, engine)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = d2plugin.HydratePluginOpts(ctx, ms, plugin)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cached[engine] = plugin.Layout
|
||||
return plugin.Layout, nil
|
||||
}
|
||||
}
|
||||
|
||||
func compile(ctx context.Context, ms *xmain.State, plugins []d2plugin.Plugin, layout *string, renderOpts d2svg.RenderOpts, fontFamily *d2fonts.FontFamily, animateInterval 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 {
|
||||
|
|
@ -341,16 +377,12 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, rende
|
|||
return nil, false, err
|
||||
}
|
||||
|
||||
layout := plugin.Layout
|
||||
opts := &d2lib.CompileOptions{
|
||||
Layout: layout,
|
||||
Ruler: ruler,
|
||||
ThemeID: renderOpts.ThemeID,
|
||||
FontFamily: fontFamily,
|
||||
InputPath: inputPath,
|
||||
}
|
||||
if renderOpts.Sketch {
|
||||
opts.FontFamily = go2.Pointer(d2fonts.HandDrawn)
|
||||
LayoutResolver: LayoutResolver(ctx, ms, plugins),
|
||||
Layout: layout,
|
||||
}
|
||||
|
||||
cancel := background.Repeat(func() {
|
||||
|
|
@ -358,12 +390,14 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, rende
|
|||
}, time.Second*5)
|
||||
defer cancel()
|
||||
|
||||
diagram, g, err := d2lib.Compile(ctx, string(input), opts)
|
||||
diagram, g, err := d2lib.Compile(ctx, string(input), opts, &renderOpts)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
cancel()
|
||||
|
||||
plugin, _ := d2plugin.FindPlugin(ctx, plugins, *opts.Layout)
|
||||
|
||||
if animateInterval > 0 {
|
||||
masterID, err := diagram.HashID()
|
||||
if err != nil {
|
||||
|
|
@ -372,6 +406,16 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, rende
|
|||
renderOpts.MasterID = masterID
|
||||
}
|
||||
|
||||
pinfo, err := plugin.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
plocation := pinfo.Type
|
||||
if pinfo.Type == "binary" {
|
||||
plocation = fmt.Sprintf("executable plugin at %s", humanPath(pinfo.Path))
|
||||
}
|
||||
ms.Log.Debug.Printf("using layout plugin %s (%s)", *opts.Layout, plocation)
|
||||
|
||||
pluginInfo, err := plugin.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
|
|
@ -805,7 +849,7 @@ func renderPDF(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, opt
|
|||
if err != nil {
|
||||
return svg, err
|
||||
}
|
||||
err = doc.AddPDFPage(pngImg, boardPath, opts.ThemeID, rootFill, diagram.Shapes, int64(opts.Pad), viewboxX, viewboxY, pageMap)
|
||||
err = doc.AddPDFPage(pngImg, boardPath, *opts.ThemeID, rootFill, diagram.Shapes, *opts.Pad, viewboxX, viewboxY, pageMap)
|
||||
if err != nil {
|
||||
return svg, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ var devMode = false
|
|||
var staticFS embed.FS
|
||||
|
||||
type watcherOpts struct {
|
||||
layoutPlugin d2plugin.Plugin
|
||||
layout *string
|
||||
plugins []d2plugin.Plugin
|
||||
renderOpts d2svg.RenderOpts
|
||||
animateInterval int64
|
||||
host string
|
||||
|
|
@ -361,7 +362,7 @@ func (w *watcher) compileLoop(ctx context.Context) error {
|
|||
w.pw = newPW
|
||||
}
|
||||
|
||||
svg, _, err := compile(ctx, w.ms, w.layoutPlugin, w.renderOpts, w.fontFamily, w.animateInterval, w.inputPath, w.outputPath, w.bundle, w.forceAppendix, w.pw.Page)
|
||||
svg, _, err := compile(ctx, w.ms, w.plugins, w.layout, w.renderOpts, w.fontFamily, w.animateInterval, w.inputPath, w.outputPath, w.bundle, w.forceAppendix, w.pw.Page)
|
||||
errs := ""
|
||||
if err != nil {
|
||||
if len(svg) > 0 {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ type CompileOptions struct {
|
|||
FS fs.FS
|
||||
}
|
||||
|
||||
func Compile(p string, r io.RuneReader, opts *CompileOptions) (*d2graph.Graph, error) {
|
||||
func Compile(p string, r io.RuneReader, opts *CompileOptions) (*d2graph.Graph, *d2target.Config, error) {
|
||||
if opts == nil {
|
||||
opts = &CompileOptions{}
|
||||
}
|
||||
|
|
@ -36,7 +36,7 @@ func Compile(p string, r io.RuneReader, opts *CompileOptions) (*d2graph.Graph, e
|
|||
UTF16: opts.UTF16,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ir, err := d2ir.Compile(ast, &d2ir.CompileOptions{
|
||||
|
|
@ -44,16 +44,16 @@ func Compile(p string, r io.RuneReader, opts *CompileOptions) (*d2graph.Graph, e
|
|||
FS: opts.FS,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
g, err := compileIR(ast, ir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, nil, err
|
||||
}
|
||||
g.SortObjectsByAST()
|
||||
g.SortEdgesByAST()
|
||||
return g, nil
|
||||
return g, compileConfig(ir), nil
|
||||
}
|
||||
|
||||
func compileIR(ast *d2ast.Map, m *d2ir.Map) (*d2graph.Graph, error) {
|
||||
|
|
@ -1285,3 +1285,45 @@ func parentSeqDiagram(n d2ir.Node) *d2ir.Map {
|
|||
n = m
|
||||
}
|
||||
}
|
||||
|
||||
func compileConfig(ir *d2ir.Map) *d2target.Config {
|
||||
f := ir.GetField("vars", "d2-config")
|
||||
if f == nil || f.Map() == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
configMap := f.Map()
|
||||
|
||||
config := &d2target.Config{}
|
||||
|
||||
f = configMap.GetField("sketch")
|
||||
if f != nil {
|
||||
val, _ := strconv.ParseBool(f.Primary().Value.ScalarString())
|
||||
config.Sketch = &val
|
||||
}
|
||||
|
||||
f = configMap.GetField("theme-id")
|
||||
if f != nil {
|
||||
val, _ := strconv.Atoi(f.Primary().Value.ScalarString())
|
||||
config.ThemeID = go2.Pointer(int64(val))
|
||||
}
|
||||
|
||||
f = configMap.GetField("dark-theme-id")
|
||||
if f != nil {
|
||||
val, _ := strconv.Atoi(f.Primary().Value.ScalarString())
|
||||
config.DarkThemeID = go2.Pointer(int64(val))
|
||||
}
|
||||
|
||||
f = configMap.GetField("pad")
|
||||
if f != nil {
|
||||
val, _ := strconv.Atoi(f.Primary().Value.ScalarString())
|
||||
config.Pad = go2.Pointer(int64(val))
|
||||
}
|
||||
|
||||
f = configMap.GetField("layout-engine")
|
||||
if f != nil {
|
||||
config.LayoutEngine = go2.Pointer(f.Primary().Value.ScalarString())
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2702,7 +2702,7 @@ object: {
|
|||
t.Parallel()
|
||||
|
||||
d2Path := fmt.Sprintf("d2/testdata/d2compiler/%v.d2", t.Name())
|
||||
g, err := d2compiler.Compile(d2Path, strings.NewReader(tc.text), nil)
|
||||
g, _, err := d2compiler.Compile(d2Path, strings.NewReader(tc.text), nil)
|
||||
if tc.expErr != "" {
|
||||
if err == nil {
|
||||
t.Fatalf("expected error with: %q", tc.expErr)
|
||||
|
|
@ -2757,7 +2757,7 @@ func testBoards(t *testing.T) {
|
|||
{
|
||||
name: "root",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `base
|
||||
g, _ := assertCompile(t, `base
|
||||
|
||||
layers: {
|
||||
one: {
|
||||
|
|
@ -2776,7 +2776,7 @@ layers: {
|
|||
{
|
||||
name: "recursive",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `base
|
||||
g, _ := assertCompile(t, `base
|
||||
|
||||
layers: {
|
||||
one: {
|
||||
|
|
@ -2804,7 +2804,7 @@ layers: {
|
|||
{
|
||||
name: "isFolderOnly",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
layers: {
|
||||
one: {
|
||||
santa
|
||||
|
|
@ -2939,7 +2939,7 @@ func testNulls(t *testing.T) {
|
|||
{
|
||||
name: "shape",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
a
|
||||
a: null
|
||||
`, "")
|
||||
|
|
@ -2949,7 +2949,7 @@ a: null
|
|||
{
|
||||
name: "edge",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
a -> b
|
||||
(a -> b)[0]: null
|
||||
`, "")
|
||||
|
|
@ -2960,7 +2960,7 @@ a -> b
|
|||
{
|
||||
name: "attribute",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
a.style.opacity: 0.2
|
||||
a.style.opacity: null
|
||||
`, "")
|
||||
|
|
@ -2992,7 +2992,7 @@ a.style.opacity: null
|
|||
{
|
||||
name: "shape",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
a
|
||||
a: null
|
||||
a
|
||||
|
|
@ -3003,7 +3003,7 @@ a
|
|||
{
|
||||
name: "edge",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
a -> b
|
||||
(a -> b)[0]: null
|
||||
a -> b
|
||||
|
|
@ -3015,7 +3015,7 @@ a -> b
|
|||
{
|
||||
name: "attribute-reset",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
a.style.opacity: 0.2
|
||||
a: null
|
||||
a
|
||||
|
|
@ -3027,7 +3027,7 @@ a
|
|||
{
|
||||
name: "edge-reset",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
a -> b
|
||||
a: null
|
||||
a
|
||||
|
|
@ -3039,7 +3039,7 @@ a
|
|||
{
|
||||
name: "children-reset",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
a.b.c
|
||||
a.b: null
|
||||
a.b
|
||||
|
|
@ -3072,7 +3072,7 @@ a.b
|
|||
{
|
||||
name: "delete-connection",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
x -> y
|
||||
y: null
|
||||
`, "")
|
||||
|
|
@ -3083,7 +3083,7 @@ y: null
|
|||
{
|
||||
name: "delete-multiple-connections",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
x -> y
|
||||
z -> y
|
||||
y -> a
|
||||
|
|
@ -3096,7 +3096,7 @@ y: null
|
|||
{
|
||||
name: "no-delete-connection",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
y: null
|
||||
x -> y
|
||||
`, "")
|
||||
|
|
@ -3107,7 +3107,7 @@ x -> y
|
|||
{
|
||||
name: "delete-children",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
x.y.z
|
||||
a.b.c
|
||||
|
||||
|
|
@ -3142,7 +3142,7 @@ a.b: null
|
|||
{
|
||||
name: "scenario",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
x
|
||||
|
||||
scenarios: {
|
||||
|
|
@ -3183,7 +3183,7 @@ func testVars(t *testing.T) {
|
|||
{
|
||||
name: "shape-label",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: im a var
|
||||
}
|
||||
|
|
@ -3196,7 +3196,7 @@ hi: ${x}
|
|||
{
|
||||
name: "style",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
primary-color: red
|
||||
}
|
||||
|
|
@ -3211,7 +3211,7 @@ hi: {
|
|||
{
|
||||
name: "number",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
columns: 2
|
||||
}
|
||||
|
|
@ -3226,7 +3226,7 @@ hi: {
|
|||
{
|
||||
name: "nested",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
colors: {
|
||||
primary: {
|
||||
|
|
@ -3244,7 +3244,7 @@ hi: {
|
|||
{
|
||||
name: "combined",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: im a var
|
||||
}
|
||||
|
|
@ -3256,7 +3256,7 @@ hi: 1 ${x} 2
|
|||
{
|
||||
name: "double-quoted",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: im a var
|
||||
}
|
||||
|
|
@ -3268,7 +3268,7 @@ hi: "1 ${x} 2"
|
|||
{
|
||||
name: "single-quoted",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: im a var
|
||||
}
|
||||
|
|
@ -3280,7 +3280,7 @@ hi: '1 ${x} 2'
|
|||
{
|
||||
name: "edge-label",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: im a var
|
||||
}
|
||||
|
|
@ -3293,7 +3293,7 @@ a -> b: ${x}
|
|||
{
|
||||
name: "edge-map",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: im a var
|
||||
}
|
||||
|
|
@ -3308,7 +3308,7 @@ a -> b: {
|
|||
{
|
||||
name: "quoted-var",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
primaryColors: {
|
||||
button: {
|
||||
|
|
@ -3330,7 +3330,7 @@ button: {
|
|||
{
|
||||
name: "quoted-var-quoted-sub",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: "hi"
|
||||
}
|
||||
|
|
@ -3343,7 +3343,7 @@ y: "hey ${x}"
|
|||
{
|
||||
name: "parent-scope",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: im root var
|
||||
}
|
||||
|
|
@ -3360,7 +3360,7 @@ a: {
|
|||
{
|
||||
name: "map",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
cool-style: {
|
||||
fill: red
|
||||
|
|
@ -3379,7 +3379,7 @@ a -> b: ${arrows}
|
|||
{
|
||||
name: "primary-and-composite",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: all {
|
||||
a: b
|
||||
|
|
@ -3395,7 +3395,7 @@ z: ${x}
|
|||
{
|
||||
name: "spread",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: all {
|
||||
a: b
|
||||
|
|
@ -3415,7 +3415,7 @@ z: {
|
|||
{
|
||||
name: "array",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
base-constraints: [UNQ; NOT NULL]
|
||||
}
|
||||
|
|
@ -3431,7 +3431,7 @@ a: {
|
|||
{
|
||||
name: "spread-array",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
base-constraints: [UNQ; NOT NULL]
|
||||
}
|
||||
|
|
@ -3447,7 +3447,7 @@ a: {
|
|||
{
|
||||
name: "sub-array",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: all
|
||||
}
|
||||
|
|
@ -3460,7 +3460,7 @@ z.class: [a; ${x}]
|
|||
{
|
||||
name: "multi-part-array",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: all
|
||||
}
|
||||
|
|
@ -3473,7 +3473,7 @@ z.class: [a; ${x}together]
|
|||
{
|
||||
name: "double-quote-primary",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: always {
|
||||
a: b
|
||||
|
|
@ -3488,7 +3488,7 @@ z: "${x} be my maybe"
|
|||
{
|
||||
name: "spread-nested",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
disclaimer: {
|
||||
I am not a lawyer
|
||||
|
|
@ -3504,7 +3504,7 @@ custom-disclaimer: DRAFT DISCLAIMER {
|
|||
{
|
||||
name: "spread-edge",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
connections: {
|
||||
x -> a
|
||||
|
|
@ -3543,7 +3543,7 @@ hi: {
|
|||
{
|
||||
name: "label",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: im a var
|
||||
}
|
||||
|
|
@ -3557,7 +3557,7 @@ hi: not a var
|
|||
{
|
||||
name: "map",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: im root var
|
||||
}
|
||||
|
|
@ -3574,7 +3574,7 @@ a: {
|
|||
{
|
||||
name: "var-in-var",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
surname: Smith
|
||||
}
|
||||
|
|
@ -3592,7 +3592,7 @@ a: {
|
|||
{
|
||||
name: "recursive-var",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: a
|
||||
}
|
||||
|
|
@ -3667,7 +3667,7 @@ a: {
|
|||
{
|
||||
name: "layer",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: im a var
|
||||
}
|
||||
|
|
@ -3685,7 +3685,7 @@ layers: {
|
|||
{
|
||||
name: "layer-2",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: root var x
|
||||
y: root var y
|
||||
|
|
@ -3710,7 +3710,7 @@ layers: {
|
|||
{
|
||||
name: "scenario",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: im a var
|
||||
}
|
||||
|
|
@ -3728,7 +3728,7 @@ scenarios: {
|
|||
{
|
||||
name: "overlay",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: im x var
|
||||
}
|
||||
|
|
@ -3763,7 +3763,7 @@ layers: {
|
|||
{
|
||||
name: "replace",
|
||||
run: func(t *testing.T) {
|
||||
g := assertCompile(t, `
|
||||
g, _ := assertCompile(t, `
|
||||
vars: {
|
||||
x: im x var
|
||||
}
|
||||
|
|
@ -3795,6 +3795,71 @@ scenarios: {
|
|||
}
|
||||
})
|
||||
|
||||
t.Run("config", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tca := []struct {
|
||||
name string
|
||||
skip bool
|
||||
run func(t *testing.T)
|
||||
}{
|
||||
{
|
||||
name: "basic",
|
||||
run: func(t *testing.T) {
|
||||
_, config := assertCompile(t, `
|
||||
vars: {
|
||||
d2-config: {
|
||||
sketch: true
|
||||
}
|
||||
}
|
||||
|
||||
x -> y
|
||||
`, "")
|
||||
assert.Equal(t, true, *config.Sketch)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid",
|
||||
run: func(t *testing.T) {
|
||||
assertCompile(t, `
|
||||
vars: {
|
||||
d2-config: {
|
||||
sketch: lol
|
||||
}
|
||||
}
|
||||
|
||||
x -> y
|
||||
`, `d2/testdata/d2compiler/TestCompile2/vars/config/invalid.d2:4:5: expected a boolean for "sketch", got "lol"`)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "not-root",
|
||||
run: func(t *testing.T) {
|
||||
assertCompile(t, `
|
||||
x: {
|
||||
vars: {
|
||||
d2-config: {
|
||||
sketch: false
|
||||
}
|
||||
}
|
||||
}
|
||||
`, `d2/testdata/d2compiler/TestCompile2/vars/config/not-root.d2:4:4: "d2-config" can only appear at root vars`)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tca {
|
||||
tc := tc
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
if tc.skip {
|
||||
t.SkipNow()
|
||||
}
|
||||
tc.run(t)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("errors", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
|
@ -3952,9 +4017,9 @@ z: {
|
|||
})
|
||||
}
|
||||
|
||||
func assertCompile(t *testing.T, text string, expErr string) *d2graph.Graph {
|
||||
func assertCompile(t *testing.T, text string, expErr string) (*d2graph.Graph, *d2target.Config) {
|
||||
d2Path := fmt.Sprintf("d2/testdata/d2compiler/%v.d2", t.Name())
|
||||
g, err := d2compiler.Compile(d2Path, strings.NewReader(text), nil)
|
||||
g, config, err := d2compiler.Compile(d2Path, strings.NewReader(text), nil)
|
||||
if expErr != "" {
|
||||
assert.Error(t, err)
|
||||
assert.ErrorString(t, err, expErr)
|
||||
|
|
@ -3972,5 +4037,5 @@ func assertCompile(t *testing.T, text string, expErr string) *d2graph.Graph {
|
|||
|
||||
err = diff.TestdataJSON(filepath.Join("..", "testdata", "d2compiler", t.Name()), got)
|
||||
assert.Success(t, err)
|
||||
return g
|
||||
return g, config
|
||||
}
|
||||
|
|
|
|||
|
|
@ -219,7 +219,7 @@ func run(t *testing.T, tc testCase) {
|
|||
ctx = log.WithTB(ctx, t, nil)
|
||||
ctx = log.Leveled(ctx, slog.LevelDebug)
|
||||
|
||||
g, err := d2compiler.Compile("", strings.NewReader(tc.dsl), &d2compiler.CompileOptions{
|
||||
g, _, err := d2compiler.Compile("", strings.NewReader(tc.dsl), &d2compiler.CompileOptions{
|
||||
UTF16: true,
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import (
|
|||
func TestSerialization(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
g, err := d2compiler.Compile("", strings.NewReader("a.a.b -> a.a.c"), nil)
|
||||
g, _, err := d2compiler.Compile("", strings.NewReader("a.a.b -> a.a.c"), nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
asserts := func(g *d2graph.Graph) {
|
||||
|
|
@ -53,7 +53,7 @@ func TestCasingRegression(t *testing.T) {
|
|||
|
||||
script := `UserCreatedTypeField`
|
||||
|
||||
g, err := d2compiler.Compile("", strings.NewReader(script), nil)
|
||||
g, _, err := d2compiler.Compile("", strings.NewReader(script), nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
_, ok := g.Root.HasChild([]string{"UserCreatedTypeField"})
|
||||
|
|
|
|||
|
|
@ -2,11 +2,14 @@ package d2ir
|
|||
|
||||
import (
|
||||
"io/fs"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"oss.terrastruct.com/d2/d2ast"
|
||||
"oss.terrastruct.com/d2/d2format"
|
||||
"oss.terrastruct.com/d2/d2parser"
|
||||
"oss.terrastruct.com/d2/d2themes"
|
||||
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
|
||||
"oss.terrastruct.com/util-go/go2"
|
||||
)
|
||||
|
||||
|
|
@ -116,6 +119,7 @@ func (c *compiler) compileSubstitutions(m *Map, varsStack []*Map) {
|
|||
// don't resolve substitutions in vars with the current scope of vars
|
||||
if f.Name == "vars" {
|
||||
c.compileSubstitutions(f.Map(), varsStack[1:])
|
||||
c.validateConfigs(f.Map().GetField("d2-config"))
|
||||
} else {
|
||||
c.compileSubstitutions(f.Map(), varsStack)
|
||||
}
|
||||
|
|
@ -131,6 +135,62 @@ func (c *compiler) compileSubstitutions(m *Map, varsStack []*Map) {
|
|||
}
|
||||
}
|
||||
|
||||
func (c *compiler) validateConfigs(configs *Field) {
|
||||
if configs == nil || configs.Map() == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if NodeBoardKind(ParentMap(ParentMap(configs))) == "" {
|
||||
c.errorf(configs.LastRef().AST(), `"%s" can only appear at root vars`, configs.Name)
|
||||
return
|
||||
}
|
||||
|
||||
for _, f := range configs.Map().Fields {
|
||||
var val string
|
||||
if f.Primary() == nil {
|
||||
if f.Name != "theme-colors" {
|
||||
c.errorf(f.LastRef().AST(), `"%s" needs a value`, f.Name)
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
val = f.Primary().Value.ScalarString()
|
||||
}
|
||||
|
||||
switch f.Name {
|
||||
case "sketch", "center":
|
||||
_, err := strconv.ParseBool(val)
|
||||
if err != nil {
|
||||
c.errorf(f.LastRef().AST(), `expected a boolean for "%s", got "%s"`, f.Name, val)
|
||||
continue
|
||||
}
|
||||
case "theme-colors":
|
||||
if f.Map() == nil {
|
||||
c.errorf(f.LastRef().AST(), `"%s" needs a map`, f.Name)
|
||||
continue
|
||||
}
|
||||
case "theme-id", "dark-theme-id":
|
||||
valInt, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
c.errorf(f.LastRef().AST(), `expected an integer for "%s", got "%s"`, f.Name, val)
|
||||
continue
|
||||
}
|
||||
if d2themescatalog.Find(int64(valInt)) == (d2themes.Theme{}) {
|
||||
c.errorf(f.LastRef().AST(), `%d is not a valid theme ID`, valInt)
|
||||
continue
|
||||
}
|
||||
case "pad":
|
||||
_, err := strconv.Atoi(val)
|
||||
if err != nil {
|
||||
c.errorf(f.LastRef().AST(), `expected an integer for "%s", got "%s"`, f.Name, val)
|
||||
continue
|
||||
}
|
||||
case "layout-engine":
|
||||
default:
|
||||
c.errorf(f.LastRef().AST(), `"%s" is not a valid config`, f.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (c *compiler) resolveSubstitutions(varsStack []*Map, node Node) {
|
||||
var subbed bool
|
||||
var resolvedField *Field
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ n2 -> n1: right to left
|
|||
n1 -> n2
|
||||
n2 -> n1
|
||||
`
|
||||
g, err := d2compiler.Compile("", strings.NewReader(input), nil)
|
||||
g, _, err := d2compiler.Compile("", strings.NewReader(input), nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
n1, has := g.Root.HasChild([]string{"n1"})
|
||||
|
|
@ -177,7 +177,7 @@ a.t2 -> b
|
|||
b -> a.t2`
|
||||
|
||||
ctx := log.WithTB(context.Background(), t, nil)
|
||||
g, err := d2compiler.Compile("", strings.NewReader(input), nil)
|
||||
g, _, err := d2compiler.Compile("", strings.NewReader(input), nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
g.Root.Shape = d2graph.Scalar{Value: d2target.ShapeSequenceDiagram}
|
||||
|
|
@ -298,7 +298,7 @@ c
|
|||
container -> c: edge 1
|
||||
`
|
||||
ctx := log.WithTB(context.Background(), t, nil)
|
||||
g, err := d2compiler.Compile("", strings.NewReader(input), nil)
|
||||
g, _, err := d2compiler.Compile("", strings.NewReader(input), nil)
|
||||
assert.Nil(t, err)
|
||||
|
||||
container, has := g.Root.HasChild([]string{"container"})
|
||||
|
|
|
|||
101
d2lib/d2.go
101
d2lib/d2.go
|
|
@ -15,8 +15,11 @@ import (
|
|||
"oss.terrastruct.com/d2/d2layouts/d2near"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2sequence"
|
||||
"oss.terrastruct.com/d2/d2renderers/d2fonts"
|
||||
"oss.terrastruct.com/d2/d2renderers/d2svg"
|
||||
"oss.terrastruct.com/d2/d2target"
|
||||
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
|
||||
"oss.terrastruct.com/d2/lib/textmeasure"
|
||||
"oss.terrastruct.com/util-go/go2"
|
||||
)
|
||||
|
||||
type CompileOptions struct {
|
||||
|
|
@ -24,8 +27,9 @@ type CompileOptions struct {
|
|||
FS fs.FS
|
||||
MeasuredTexts []*d2target.MText
|
||||
Ruler *textmeasure.Ruler
|
||||
Layout func(context.Context, *d2graph.Graph) error
|
||||
ThemeID int64
|
||||
LayoutResolver func(engine string) (d2graph.LayoutGraph, error)
|
||||
|
||||
Layout *string
|
||||
|
||||
// FontFamily controls the font family used for all texts that are not the following:
|
||||
// - code
|
||||
|
|
@ -37,39 +41,42 @@ type CompileOptions struct {
|
|||
InputPath string
|
||||
}
|
||||
|
||||
func Compile(ctx context.Context, input string, opts *CompileOptions) (*d2target.Diagram, *d2graph.Graph, error) {
|
||||
if opts == nil {
|
||||
opts = &CompileOptions{}
|
||||
func Compile(ctx context.Context, input string, compileOpts *CompileOptions, renderOpts *d2svg.RenderOpts) (*d2target.Diagram, *d2graph.Graph, error) {
|
||||
if compileOpts == nil {
|
||||
compileOpts = &CompileOptions{}
|
||||
}
|
||||
if renderOpts == nil {
|
||||
renderOpts = &d2svg.RenderOpts{}
|
||||
}
|
||||
|
||||
g, err := d2compiler.Compile(opts.InputPath, strings.NewReader(input), &d2compiler.CompileOptions{
|
||||
UTF16: opts.UTF16,
|
||||
FS: opts.FS,
|
||||
g, config, err := d2compiler.Compile(compileOpts.InputPath, strings.NewReader(input), &d2compiler.CompileOptions{
|
||||
UTF16: compileOpts.UTF16,
|
||||
FS: compileOpts.FS,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
d, err := compile(ctx, g, opts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return d, g, nil
|
||||
applyConfigs(config, compileOpts, renderOpts)
|
||||
applyDefaults(compileOpts, renderOpts)
|
||||
|
||||
d, err := compile(ctx, g, compileOpts, renderOpts)
|
||||
return d, g, err
|
||||
}
|
||||
|
||||
func compile(ctx context.Context, g *d2graph.Graph, opts *CompileOptions) (*d2target.Diagram, error) {
|
||||
err := g.ApplyTheme(opts.ThemeID)
|
||||
func compile(ctx context.Context, g *d2graph.Graph, compileOpts *CompileOptions, renderOpts *d2svg.RenderOpts) (*d2target.Diagram, error) {
|
||||
err := g.ApplyTheme(*renderOpts.ThemeID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(g.Objects) > 0 {
|
||||
err := g.SetDimensions(opts.MeasuredTexts, opts.Ruler, opts.FontFamily)
|
||||
err := g.SetDimensions(compileOpts.MeasuredTexts, compileOpts.Ruler, compileOpts.FontFamily)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
coreLayout, err := getLayout(opts)
|
||||
coreLayout, err := getLayout(compileOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -96,27 +103,27 @@ func compile(ctx context.Context, g *d2graph.Graph, opts *CompileOptions) (*d2ta
|
|||
}
|
||||
}
|
||||
|
||||
d, err := d2exporter.Export(ctx, g, opts.FontFamily)
|
||||
d, err := d2exporter.Export(ctx, g, compileOpts.FontFamily)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, l := range g.Layers {
|
||||
ld, err := compile(ctx, l, opts)
|
||||
ld, err := compile(ctx, l, compileOpts, renderOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.Layers = append(d.Layers, ld)
|
||||
}
|
||||
for _, l := range g.Scenarios {
|
||||
ld, err := compile(ctx, l, opts)
|
||||
ld, err := compile(ctx, l, compileOpts, renderOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.Scenarios = append(d.Scenarios, ld)
|
||||
}
|
||||
for _, l := range g.Steps {
|
||||
ld, err := compile(ctx, l, opts)
|
||||
ld, err := compile(ctx, l, compileOpts, renderOpts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -127,7 +134,7 @@ func compile(ctx context.Context, g *d2graph.Graph, opts *CompileOptions) (*d2ta
|
|||
|
||||
func getLayout(opts *CompileOptions) (d2graph.LayoutGraph, error) {
|
||||
if opts.Layout != nil {
|
||||
return opts.Layout, nil
|
||||
return opts.LayoutResolver(*opts.Layout)
|
||||
} else if os.Getenv("D2_LAYOUT") == "dagre" {
|
||||
defaultLayout := func(ctx context.Context, g *d2graph.Graph) error {
|
||||
return d2dagrelayout.Layout(ctx, g, nil)
|
||||
|
|
@ -137,3 +144,53 @@ func getLayout(opts *CompileOptions) (d2graph.LayoutGraph, error) {
|
|||
return nil, errors.New("no available layout")
|
||||
}
|
||||
}
|
||||
|
||||
// applyConfigs applies the configs read from D2 and applies it to passed in opts
|
||||
// It will only write to opt fields that are nil, as passed-in opts have precedence
|
||||
func applyConfigs(config *d2target.Config, compileOpts *CompileOptions, renderOpts *d2svg.RenderOpts) {
|
||||
if config == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if compileOpts.Layout == nil {
|
||||
compileOpts.Layout = config.LayoutEngine
|
||||
}
|
||||
|
||||
if renderOpts.ThemeID == nil {
|
||||
renderOpts.ThemeID = config.ThemeID
|
||||
}
|
||||
if renderOpts.DarkThemeID == nil {
|
||||
renderOpts.DarkThemeID = config.DarkThemeID
|
||||
}
|
||||
if renderOpts.Sketch == nil {
|
||||
renderOpts.Sketch = config.Sketch
|
||||
}
|
||||
if renderOpts.Pad == nil {
|
||||
renderOpts.Pad = config.Pad
|
||||
}
|
||||
if renderOpts.Center == nil {
|
||||
renderOpts.Center = config.Center
|
||||
}
|
||||
}
|
||||
|
||||
func applyDefaults(compileOpts *CompileOptions, renderOpts *d2svg.RenderOpts) {
|
||||
if compileOpts.Layout == nil {
|
||||
compileOpts.Layout = go2.Pointer("dagre")
|
||||
}
|
||||
|
||||
if renderOpts.ThemeID == nil {
|
||||
renderOpts.ThemeID = &d2themescatalog.NeutralDefault.ID
|
||||
}
|
||||
if renderOpts.Sketch == nil {
|
||||
renderOpts.Sketch = go2.Pointer(false)
|
||||
}
|
||||
if *renderOpts.Sketch {
|
||||
compileOpts.FontFamily = go2.Pointer(d2fonts.HandDrawn)
|
||||
}
|
||||
if renderOpts.Pad == nil {
|
||||
renderOpts.Pad = go2.Pointer(int64(d2svg.DEFAULT_PADDING))
|
||||
}
|
||||
if renderOpts.Center == nil {
|
||||
renderOpts.Center = go2.Pointer(false)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -305,7 +305,7 @@ func pathFromScopeObj(g *d2graph.Graph, key *d2ast.Key, fromScope *d2graph.Objec
|
|||
|
||||
func recompile(ast *d2ast.Map) (*d2graph.Graph, error) {
|
||||
s := d2format.Format(ast)
|
||||
g, err := d2compiler.Compile(ast.Range.Path, strings.NewReader(s), nil)
|
||||
g, _, err := d2compiler.Compile(ast.Range.Path, strings.NewReader(s), nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to recompile:\n%s\n%w", s, err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7002,7 +7002,7 @@ type editTest struct {
|
|||
|
||||
func (tc editTest) run(t *testing.T) {
|
||||
d2Path := fmt.Sprintf("d2/testdata/d2oracle/%v.d2", t.Name())
|
||||
g, err := d2compiler.Compile(d2Path, strings.NewReader(tc.text), nil)
|
||||
g, _, err := d2compiler.Compile(d2Path, strings.NewReader(tc.text), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -7265,7 +7265,7 @@ scenarios: {
|
|||
t.Parallel()
|
||||
|
||||
d2Path := fmt.Sprintf("d2/testdata/d2oracle/%v.d2", t.Name())
|
||||
g, err := d2compiler.Compile(d2Path, strings.NewReader(tc.text), nil)
|
||||
g, _, err := d2compiler.Compile(d2Path, strings.NewReader(tc.text), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -7725,7 +7725,7 @@ z
|
|||
t.Parallel()
|
||||
|
||||
d2Path := fmt.Sprintf("d2/testdata/d2oracle/%v.d2", t.Name())
|
||||
g, err := d2compiler.Compile(d2Path, strings.NewReader(tc.text), nil)
|
||||
g, _, err := d2compiler.Compile(d2Path, strings.NewReader(tc.text), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -8095,7 +8095,7 @@ layers: {
|
|||
t.Parallel()
|
||||
|
||||
d2Path := fmt.Sprintf("d2/testdata/d2oracle/%v.d2", t.Name())
|
||||
g, err := d2compiler.Compile(d2Path, strings.NewReader(tc.text), nil)
|
||||
g, _, err := d2compiler.Compile(d2Path, strings.NewReader(tc.text), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
@ -8325,7 +8325,7 @@ scenarios: {
|
|||
t.Parallel()
|
||||
|
||||
d2Path := fmt.Sprintf("d2/testdata/d2oracle/%v.d2", t.Name())
|
||||
g, err := d2compiler.Compile(d2Path, strings.NewReader(tc.text), nil)
|
||||
g, _, err := d2compiler.Compile(d2Path, strings.NewReader(tc.text), nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,10 +49,10 @@ func Wrap(rootDiagram *d2target.Diagram, svgs [][]byte, renderOpts d2svg.RenderO
|
|||
// TODO account for stroke width of root border
|
||||
|
||||
tl, br := rootDiagram.NestedBoundingBox()
|
||||
left := tl.X - renderOpts.Pad
|
||||
top := tl.Y - renderOpts.Pad
|
||||
width := br.X - tl.X + renderOpts.Pad*2
|
||||
height := br.Y - tl.Y + renderOpts.Pad*2
|
||||
left := tl.X - int(*renderOpts.Pad)
|
||||
top := tl.Y - int(*renderOpts.Pad)
|
||||
width := br.X - tl.X + int(*renderOpts.Pad)*2
|
||||
height := br.Y - tl.Y + int(*renderOpts.Pad)*2
|
||||
|
||||
fitToScreenWrapperOpening := fmt.Sprintf(`<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="%s" preserveAspectRatio="xMinYMin meet" viewBox="0 0 %d %d">`,
|
||||
version.Version,
|
||||
|
|
@ -93,7 +93,7 @@ func Wrap(rootDiagram *d2target.Diagram, svgs [][]byte, renderOpts d2svg.RenderO
|
|||
fmt.Fprintf(buf, `<style type="text/css">%s</style>`, css)
|
||||
}
|
||||
|
||||
if renderOpts.Sketch {
|
||||
if renderOpts.Sketch != nil && *renderOpts.Sketch {
|
||||
d2sketch.DefineFillPatterns(buf)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import (
|
|||
"oss.terrastruct.com/util-go/diff"
|
||||
"oss.terrastruct.com/util-go/go2"
|
||||
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2elklayout"
|
||||
"oss.terrastruct.com/d2/d2lib"
|
||||
|
|
@ -1339,16 +1340,22 @@ func run(t *testing.T, tc testCase) {
|
|||
return
|
||||
}
|
||||
|
||||
layout := d2dagrelayout.DefaultLayout
|
||||
if strings.EqualFold(tc.engine, "elk") {
|
||||
layout = d2elklayout.DefaultLayout
|
||||
layoutResolver := func(engine string) (d2graph.LayoutGraph, error) {
|
||||
if strings.EqualFold(engine, "elk") {
|
||||
return d2elklayout.DefaultLayout, nil
|
||||
}
|
||||
return d2dagrelayout.DefaultLayout, nil
|
||||
}
|
||||
renderOpts := &d2svg.RenderOpts{
|
||||
Sketch: go2.Pointer(true),
|
||||
ThemeID: go2.Pointer(tc.themeID),
|
||||
}
|
||||
diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
|
||||
Ruler: ruler,
|
||||
Layout: layout,
|
||||
Layout: &tc.engine,
|
||||
LayoutResolver: layoutResolver,
|
||||
FontFamily: go2.Pointer(d2fonts.HandDrawn),
|
||||
ThemeID: tc.themeID,
|
||||
})
|
||||
}, renderOpts)
|
||||
if !tassert.Nil(t, err) {
|
||||
return
|
||||
}
|
||||
|
|
@ -1356,11 +1363,7 @@ func run(t *testing.T, tc testCase) {
|
|||
dataPath := filepath.Join("testdata", strings.TrimPrefix(t.Name(), "TestSketch/"))
|
||||
pathGotSVG := filepath.Join(dataPath, "sketch.got.svg")
|
||||
|
||||
svgBytes, err := d2svg.Render(diagram, &d2svg.RenderOpts{
|
||||
Pad: d2svg.DEFAULT_PADDING,
|
||||
Sketch: true,
|
||||
ThemeID: tc.themeID,
|
||||
})
|
||||
svgBytes, err := d2svg.Render(diagram, renderOpts)
|
||||
assert.Success(t, err)
|
||||
err = os.MkdirAll(dataPath, 0755)
|
||||
assert.Success(t, err)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import (
|
|||
"oss.terrastruct.com/util-go/assert"
|
||||
"oss.terrastruct.com/util-go/diff"
|
||||
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||
"oss.terrastruct.com/d2/d2lib"
|
||||
"oss.terrastruct.com/d2/d2renderers/d2svg"
|
||||
|
|
@ -152,11 +153,17 @@ func run(t *testing.T, tc testCase) {
|
|||
return
|
||||
}
|
||||
|
||||
renderOpts := &d2svg.RenderOpts{
|
||||
ThemeID: &tc.themeID,
|
||||
}
|
||||
|
||||
layoutResolver := func(engine string) (d2graph.LayoutGraph, error) {
|
||||
return d2dagrelayout.DefaultLayout, nil
|
||||
}
|
||||
diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
|
||||
Ruler: ruler,
|
||||
Layout: d2dagrelayout.DefaultLayout,
|
||||
ThemeID: tc.themeID,
|
||||
})
|
||||
LayoutResolver: layoutResolver,
|
||||
}, renderOpts)
|
||||
if !tassert.Nil(t, err) {
|
||||
return
|
||||
}
|
||||
|
|
@ -164,10 +171,7 @@ func run(t *testing.T, tc testCase) {
|
|||
dataPath := filepath.Join("testdata", strings.TrimPrefix(t.Name(), "TestAppendix/"))
|
||||
pathGotSVG := filepath.Join(dataPath, "sketch.got.svg")
|
||||
|
||||
svgBytes, err := d2svg.Render(diagram, &d2svg.RenderOpts{
|
||||
Pad: d2svg.DEFAULT_PADDING,
|
||||
ThemeID: tc.themeID,
|
||||
})
|
||||
svgBytes, err := d2svg.Render(diagram, renderOpts)
|
||||
assert.Success(t, err)
|
||||
svgBytes = appendix.Append(diagram, ruler, svgBytes)
|
||||
|
||||
|
|
|
|||
|
|
@ -69,10 +69,10 @@ var grain string
|
|||
var paper string
|
||||
|
||||
type RenderOpts struct {
|
||||
Pad int
|
||||
Sketch bool
|
||||
Center bool
|
||||
ThemeID int64
|
||||
Pad *int64
|
||||
Sketch *bool
|
||||
Center *bool
|
||||
ThemeID *int64
|
||||
DarkThemeID *int64
|
||||
Font string
|
||||
// the svg will be scaled by this factor, if unset the svg will fit to screen
|
||||
|
|
@ -1689,26 +1689,28 @@ func appendOnTrigger(buf *bytes.Buffer, source string, triggers []string, newCon
|
|||
}
|
||||
}
|
||||
|
||||
const DEFAULT_THEME int64 = 0
|
||||
|
||||
var DEFAULT_DARK_THEME *int64 = nil // no theme selected
|
||||
|
||||
func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
|
||||
var sketchRunner *d2sketch.Runner
|
||||
pad := DEFAULT_PADDING
|
||||
themeID := DEFAULT_THEME
|
||||
themeID := d2themescatalog.NeutralDefault.ID
|
||||
darkThemeID := DEFAULT_DARK_THEME
|
||||
var scale *float64
|
||||
if opts != nil {
|
||||
pad = opts.Pad
|
||||
if opts.Sketch {
|
||||
if opts.Pad != nil {
|
||||
pad = int(*opts.Pad)
|
||||
}
|
||||
if opts.Sketch != nil && *opts.Sketch {
|
||||
var err error
|
||||
sketchRunner, err = d2sketch.InitSketchVM()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
themeID = opts.ThemeID
|
||||
if opts.ThemeID != nil {
|
||||
themeID = *opts.ThemeID
|
||||
}
|
||||
darkThemeID = opts.DarkThemeID
|
||||
scale = opts.Scale
|
||||
}
|
||||
|
|
@ -1792,7 +1794,7 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
|
|||
upperBuf := &bytes.Buffer{}
|
||||
if opts.MasterID == "" {
|
||||
EmbedFonts(upperBuf, diagramHash, buf.String(), diagram.FontFamily, diagram.GetCorpus()) // EmbedFonts *must* run before `d2sketch.DefineFillPatterns`, but after all elements are appended to `buf`
|
||||
themeStylesheet, err := ThemeCSS(diagramHash, themeID, darkThemeID)
|
||||
themeStylesheet, err := ThemeCSS(diagramHash, &themeID, darkThemeID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1913,7 +1915,7 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
|
|||
}
|
||||
|
||||
alignment := "xMinYMin"
|
||||
if opts.Center {
|
||||
if opts.Center != nil && *opts.Center {
|
||||
alignment = "xMidYMid"
|
||||
}
|
||||
fitToScreenWrapperOpening := ""
|
||||
|
|
@ -1954,8 +1956,11 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
|
|||
}
|
||||
|
||||
// TODO include only colors that are being used to reduce size
|
||||
func ThemeCSS(diagramHash string, themeID int64, darkThemeID *int64) (stylesheet string, err error) {
|
||||
out, err := singleThemeRulesets(diagramHash, themeID)
|
||||
func ThemeCSS(diagramHash string, themeID *int64, darkThemeID *int64) (stylesheet string, err error) {
|
||||
if themeID == nil {
|
||||
themeID = &d2themescatalog.NeutralDefault.ID
|
||||
}
|
||||
out, err := singleThemeRulesets(diagramHash, *themeID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import (
|
|||
"oss.terrastruct.com/util-go/diff"
|
||||
"oss.terrastruct.com/util-go/go2"
|
||||
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||
"oss.terrastruct.com/d2/d2lib"
|
||||
"oss.terrastruct.com/d2/d2renderers/d2fonts"
|
||||
|
|
@ -426,12 +427,18 @@ func run(t *testing.T, tc testCase) {
|
|||
return
|
||||
}
|
||||
|
||||
renderOpts := &d2svg.RenderOpts{
|
||||
ThemeID: go2.Pointer(int64(200)),
|
||||
}
|
||||
layoutResolver := func(engine string) (d2graph.LayoutGraph, error) {
|
||||
return d2dagrelayout.DefaultLayout, nil
|
||||
}
|
||||
|
||||
diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
|
||||
Ruler: ruler,
|
||||
Layout: d2dagrelayout.DefaultLayout,
|
||||
LayoutResolver: layoutResolver,
|
||||
FontFamily: go2.Pointer(d2fonts.HandDrawn),
|
||||
ThemeID: 200,
|
||||
})
|
||||
}, renderOpts)
|
||||
if !tassert.Nil(t, err) {
|
||||
return
|
||||
}
|
||||
|
|
@ -439,10 +446,7 @@ func run(t *testing.T, tc testCase) {
|
|||
dataPath := filepath.Join("testdata", strings.TrimPrefix(t.Name(), "TestDarkTheme/"))
|
||||
pathGotSVG := filepath.Join(dataPath, "dark_theme.got.svg")
|
||||
|
||||
svgBytes, err := d2svg.Render(diagram, &d2svg.RenderOpts{
|
||||
Pad: d2svg.DEFAULT_PADDING,
|
||||
ThemeID: 200,
|
||||
})
|
||||
svgBytes, err := d2svg.Render(diagram, renderOpts)
|
||||
assert.Success(t, err)
|
||||
err = os.MkdirAll(dataPath, 0755)
|
||||
assert.Success(t, err)
|
||||
|
|
|
|||
|
|
@ -38,8 +38,24 @@ const (
|
|||
|
||||
var BorderOffset = geo.NewVector(5, 5)
|
||||
|
||||
type Config struct {
|
||||
Sketch *bool `json:"sketch"`
|
||||
ThemeID *int64 `json:"themeID"`
|
||||
DarkThemeID *int64 `json:"darkThemeID"`
|
||||
Pad *int64 `json:"pad"`
|
||||
Center *bool `json:"center"`
|
||||
LayoutEngine *string `json:"layoutEngine"`
|
||||
ThemeOverrides *ThemeOverrides `json:"themeOverrides"`
|
||||
}
|
||||
|
||||
type ThemeOverrides struct {
|
||||
N1 *string `json:"n1"`
|
||||
// TODO
|
||||
}
|
||||
|
||||
type Diagram struct {
|
||||
Name string `json:"name"`
|
||||
Config *Config `json:"config,omitempty"`
|
||||
// See docs on the same field in d2graph to understand what it means.
|
||||
IsFolderOnly bool `json:"isFolderOnly"`
|
||||
Description string `json:"description,omitempty"`
|
||||
|
|
|
|||
|
|
@ -11,21 +11,24 @@ import (
|
|||
"oss.terrastruct.com/d2/d2renderers/d2svg"
|
||||
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
|
||||
"oss.terrastruct.com/d2/lib/textmeasure"
|
||||
"oss.terrastruct.com/util-go/go2"
|
||||
)
|
||||
|
||||
// Remember to add if err != nil checks in production.
|
||||
func main() {
|
||||
ruler, _ := textmeasure.NewRuler()
|
||||
defaultLayout := func(ctx context.Context, g *d2graph.Graph) error {
|
||||
return d2dagrelayout.Layout(ctx, g, nil)
|
||||
layoutResolver := func(engine string) (d2graph.LayoutGraph, error) {
|
||||
return d2dagrelayout.DefaultLayout, nil
|
||||
}
|
||||
diagram, _, _ := d2lib.Compile(context.Background(), "x -> y", &d2lib.CompileOptions{
|
||||
Layout: defaultLayout,
|
||||
renderOpts := &d2svg.RenderOpts{
|
||||
Pad: go2.Pointer(int64(5)),
|
||||
ThemeID: &d2themescatalog.GrapeSoda.ID,
|
||||
}
|
||||
compileOpts := &d2lib.CompileOptions{
|
||||
LayoutResolver: layoutResolver,
|
||||
Ruler: ruler,
|
||||
})
|
||||
out, _ := d2svg.Render(diagram, &d2svg.RenderOpts{
|
||||
Pad: d2svg.DEFAULT_PADDING,
|
||||
ThemeID: d2themescatalog.GrapeSoda.ID,
|
||||
})
|
||||
}
|
||||
diagram, _, _ := d2lib.Compile(context.Background(), "x -> y", compileOpts, renderOpts)
|
||||
out, _ := d2svg.Render(diagram, renderOpts)
|
||||
_ = ioutil.WriteFile(filepath.Join("out.svg"), out, 0600)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"fmt"
|
||||
|
||||
"oss.terrastruct.com/d2/d2format"
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||
"oss.terrastruct.com/d2/d2lib"
|
||||
"oss.terrastruct.com/d2/d2oracle"
|
||||
|
|
@ -15,10 +16,14 @@ import (
|
|||
func main() {
|
||||
// From one.go
|
||||
ruler, _ := textmeasure.NewRuler()
|
||||
_, graph, _ := d2lib.Compile(context.Background(), "x -> y", &d2lib.CompileOptions{
|
||||
Layout: d2dagrelayout.DefaultLayout,
|
||||
layoutResolver := func(engine string) (d2graph.LayoutGraph, error) {
|
||||
return d2dagrelayout.DefaultLayout, nil
|
||||
}
|
||||
compileOpts := &d2lib.CompileOptions{
|
||||
LayoutResolver: layoutResolver,
|
||||
Ruler: ruler,
|
||||
})
|
||||
}
|
||||
_, graph, _ := d2lib.Compile(context.Background(), "x -> y", compileOpts, nil)
|
||||
|
||||
// Create a shape with the ID, "meow"
|
||||
graph, _, _ = d2oracle.Create(graph, nil, "meow")
|
||||
|
|
|
|||
|
|
@ -16,15 +16,14 @@ import (
|
|||
|
||||
// Remember to add if err != nil checks in production.
|
||||
func main() {
|
||||
graph, _ := d2compiler.Compile("", strings.NewReader("x -> y"), nil)
|
||||
graph, _, _ := d2compiler.Compile("", strings.NewReader("x -> y"), nil)
|
||||
graph.ApplyTheme(d2themescatalog.NeutralDefault.ID)
|
||||
ruler, _ := textmeasure.NewRuler()
|
||||
_ = graph.SetDimensions(nil, ruler, nil)
|
||||
_ = d2dagrelayout.Layout(context.Background(), graph, nil)
|
||||
diagram, _ := d2exporter.Export(context.Background(), graph, nil)
|
||||
out, _ := d2svg.Render(diagram, &d2svg.RenderOpts{
|
||||
Pad: d2svg.DEFAULT_PADDING,
|
||||
ThemeID: d2themescatalog.NeutralDefault.ID,
|
||||
ThemeID: &d2themescatalog.NeutralDefault.ID,
|
||||
})
|
||||
_ = ioutil.WriteFile(filepath.Join("out.svg"), out, 0600)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,6 +111,38 @@ func TestCLI_E2E(t *testing.T) {
|
|||
shape: text
|
||||
}
|
||||
|
||||
steps: {
|
||||
1: {
|
||||
Approach road
|
||||
}
|
||||
2: {
|
||||
Approach road -> Cross road
|
||||
}
|
||||
3: {
|
||||
Cross road -> Make you wonder why
|
||||
}
|
||||
}
|
||||
`)
|
||||
err := runTestMain(t, ctx, dir, env, "--animate-interval=1400", "animation.d2")
|
||||
assert.Success(t, err)
|
||||
svg := readFile(t, dir, "animation.svg")
|
||||
assert.Testdata(t, ".svg", svg)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vars-animation",
|
||||
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
|
||||
writeFile(t, dir, "animation.d2", `vars: {
|
||||
d2-config: {
|
||||
theme-id: 300
|
||||
}
|
||||
}
|
||||
Chicken's plan: {
|
||||
style.font-size: 35
|
||||
near: top-center
|
||||
shape: text
|
||||
}
|
||||
|
||||
steps: {
|
||||
1: {
|
||||
Approach road
|
||||
|
|
@ -404,6 +436,17 @@ steps: {
|
|||
assert.Testdata(t, ".svg", svg)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "import_vars",
|
||||
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
|
||||
writeFile(t, dir, "hello-world.d2", `vars: { d2-config: @config }; x -> y`)
|
||||
writeFile(t, dir, "config.d2", `theme-id: 200`)
|
||||
err := runTestMain(t, ctx, dir, env, filepath.Join(dir, "hello-world.d2"))
|
||||
assert.Success(t, err)
|
||||
svg := readFile(t, dir, "hello-world.svg")
|
||||
assert.Testdata(t, ".svg", svg)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "import_spread_nested",
|
||||
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
|
||||
|
|
@ -449,6 +492,26 @@ steps: {
|
|||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vars-config",
|
||||
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
|
||||
writeFile(t, dir, "hello-world.d2", `vars: {
|
||||
d2-config: {
|
||||
sketch: true
|
||||
layout-engine: elk
|
||||
}
|
||||
}
|
||||
x -> y -> a.dream
|
||||
it -> was -> all -> a.dream
|
||||
i used to read
|
||||
`)
|
||||
env.Setenv("D2_THEME", "1")
|
||||
err := runTestMain(t, ctx, dir, env, "--pad=10", "hello-world.d2")
|
||||
assert.Success(t, err)
|
||||
svg := readFile(t, dir, "hello-world.svg")
|
||||
assert.Testdata(t, ".svg", svg)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
|
|
|||
96
e2etests-cli/testdata/TestCLI_E2E/import_vars.exp.svg
vendored
Normal file
96
e2etests-cli/testdata/TestCLI_E2E/import_vars.exp.svg
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.5.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 256 434"><svg id="d2-svg" class="d2-855222762" width="256" height="434" viewBox="-101 -101 256 434"><rect x="-101.000000" y="-101.000000" width="256.000000" height="434.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||
.d2-855222762 .text-bold {
|
||||
font-family: "d2-855222762-font-bold";
|
||||
}
|
||||
@font-face {
|
||||
font-family: d2-855222762-font-bold;
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||
shape-rendering: geometricPrecision;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.connection {
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.blend {
|
||||
mix-blend-mode: multiply;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.d2-855222762 .fill-N1{fill:#CDD6F4;}
|
||||
.d2-855222762 .fill-N2{fill:#BAC2DE;}
|
||||
.d2-855222762 .fill-N3{fill:#A6ADC8;}
|
||||
.d2-855222762 .fill-N4{fill:#585B70;}
|
||||
.d2-855222762 .fill-N5{fill:#45475A;}
|
||||
.d2-855222762 .fill-N6{fill:#313244;}
|
||||
.d2-855222762 .fill-N7{fill:#1E1E2E;}
|
||||
.d2-855222762 .fill-B1{fill:#CBA6f7;}
|
||||
.d2-855222762 .fill-B2{fill:#CBA6f7;}
|
||||
.d2-855222762 .fill-B3{fill:#6C7086;}
|
||||
.d2-855222762 .fill-B4{fill:#585B70;}
|
||||
.d2-855222762 .fill-B5{fill:#45475A;}
|
||||
.d2-855222762 .fill-B6{fill:#313244;}
|
||||
.d2-855222762 .fill-AA2{fill:#f38BA8;}
|
||||
.d2-855222762 .fill-AA4{fill:#45475A;}
|
||||
.d2-855222762 .fill-AA5{fill:#313244;}
|
||||
.d2-855222762 .fill-AB4{fill:#45475A;}
|
||||
.d2-855222762 .fill-AB5{fill:#313244;}
|
||||
.d2-855222762 .stroke-N1{stroke:#CDD6F4;}
|
||||
.d2-855222762 .stroke-N2{stroke:#BAC2DE;}
|
||||
.d2-855222762 .stroke-N3{stroke:#A6ADC8;}
|
||||
.d2-855222762 .stroke-N4{stroke:#585B70;}
|
||||
.d2-855222762 .stroke-N5{stroke:#45475A;}
|
||||
.d2-855222762 .stroke-N6{stroke:#313244;}
|
||||
.d2-855222762 .stroke-N7{stroke:#1E1E2E;}
|
||||
.d2-855222762 .stroke-B1{stroke:#CBA6f7;}
|
||||
.d2-855222762 .stroke-B2{stroke:#CBA6f7;}
|
||||
.d2-855222762 .stroke-B3{stroke:#6C7086;}
|
||||
.d2-855222762 .stroke-B4{stroke:#585B70;}
|
||||
.d2-855222762 .stroke-B5{stroke:#45475A;}
|
||||
.d2-855222762 .stroke-B6{stroke:#313244;}
|
||||
.d2-855222762 .stroke-AA2{stroke:#f38BA8;}
|
||||
.d2-855222762 .stroke-AA4{stroke:#45475A;}
|
||||
.d2-855222762 .stroke-AA5{stroke:#313244;}
|
||||
.d2-855222762 .stroke-AB4{stroke:#45475A;}
|
||||
.d2-855222762 .stroke-AB5{stroke:#313244;}
|
||||
.d2-855222762 .background-color-N1{background-color:#CDD6F4;}
|
||||
.d2-855222762 .background-color-N2{background-color:#BAC2DE;}
|
||||
.d2-855222762 .background-color-N3{background-color:#A6ADC8;}
|
||||
.d2-855222762 .background-color-N4{background-color:#585B70;}
|
||||
.d2-855222762 .background-color-N5{background-color:#45475A;}
|
||||
.d2-855222762 .background-color-N6{background-color:#313244;}
|
||||
.d2-855222762 .background-color-N7{background-color:#1E1E2E;}
|
||||
.d2-855222762 .background-color-B1{background-color:#CBA6f7;}
|
||||
.d2-855222762 .background-color-B2{background-color:#CBA6f7;}
|
||||
.d2-855222762 .background-color-B3{background-color:#6C7086;}
|
||||
.d2-855222762 .background-color-B4{background-color:#585B70;}
|
||||
.d2-855222762 .background-color-B5{background-color:#45475A;}
|
||||
.d2-855222762 .background-color-B6{background-color:#313244;}
|
||||
.d2-855222762 .background-color-AA2{background-color:#f38BA8;}
|
||||
.d2-855222762 .background-color-AA4{background-color:#45475A;}
|
||||
.d2-855222762 .background-color-AA5{background-color:#313244;}
|
||||
.d2-855222762 .background-color-AB4{background-color:#45475A;}
|
||||
.d2-855222762 .background-color-AB5{background-color:#313244;}
|
||||
.d2-855222762 .color-N1{color:#CDD6F4;}
|
||||
.d2-855222762 .color-N2{color:#BAC2DE;}
|
||||
.d2-855222762 .color-N3{color:#A6ADC8;}
|
||||
.d2-855222762 .color-N4{color:#585B70;}
|
||||
.d2-855222762 .color-N5{color:#45475A;}
|
||||
.d2-855222762 .color-N6{color:#313244;}
|
||||
.d2-855222762 .color-N7{color:#1E1E2E;}
|
||||
.d2-855222762 .color-B1{color:#CBA6f7;}
|
||||
.d2-855222762 .color-B2{color:#CBA6f7;}
|
||||
.d2-855222762 .color-B3{color:#6C7086;}
|
||||
.d2-855222762 .color-B4{color:#585B70;}
|
||||
.d2-855222762 .color-B5{color:#45475A;}
|
||||
.d2-855222762 .color-B6{color:#313244;}
|
||||
.d2-855222762 .color-AA2{color:#f38BA8;}
|
||||
.d2-855222762 .color-AA4{color:#45475A;}
|
||||
.d2-855222762 .color-AA5{color:#313244;}
|
||||
.d2-855222762 .color-AB4{color:#45475A;}
|
||||
.d2-855222762 .color-AB5{color:#313244;}.appendix text.text{fill:#CDD6F4}.md{--color-fg-default:#CDD6F4;--color-fg-muted:#BAC2DE;--color-fg-subtle:#A6ADC8;--color-canvas-default:#1E1E2E;--color-canvas-subtle:#313244;--color-border-default:#CBA6f7;--color-border-muted:#CBA6f7;--color-neutral-muted:#313244;--color-accent-fg:#CBA6f7;--color-accent-emphasis:#CBA6f7;--color-attention-subtle:#BAC2DE;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-B2{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-B3{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-B4{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-B5{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B6{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-AA2{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-AA4{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-AA5{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-AB4{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-AB5{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N1{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N2{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-N5{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N6{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N7{fill:url(#streaks-darker);mix-blend-mode:lighten}.light-code{display: none}.dark-code{display: block}]]></style><g id="x"><g class="shape" ><rect x="1.000000" y="0.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="27.500000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">x</text></g><g id="y"><g class="shape" ><rect x="0.000000" y="166.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="27.000000" y="204.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">y</text></g><g id="(x -> y)[0]"><marker id="mk-3488378134" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" class="connection fill-B1" stroke-width="2" /> </marker><path d="M 27.000000 68.000000 C 27.000000 106.000000 27.000000 126.000000 27.000000 162.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-855222762)" /></g><mask id="d2-855222762" maskUnits="userSpaceOnUse" x="-101" y="-101" width="256" height="434">
|
||||
<rect x="-101" y="-101" width="256" height="434" fill="white"></rect>
|
||||
<rect x="23.500000" y="22.500000" width="8" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||
<rect x="22.500000" y="188.500000" width="9" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||
</mask></svg></svg>
|
||||
|
After Width: | Height: | Size: 9.4 KiB |
Binary file not shown.
883
e2etests-cli/testdata/TestCLI_E2E/vars-animation.exp.svg
vendored
Normal file
883
e2etests-cli/testdata/TestCLI_E2E/vars-animation.exp.svg
vendored
Normal file
|
|
@ -0,0 +1,883 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" d2Version="v0.5.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 606 665"><svg id="d2-svg" width="606" height="665" viewBox="-246 -166 606 665"><style type="text/css"><![CDATA[
|
||||
.d2-1132014075 .text-mono {
|
||||
font-family: "d2-1132014075-font-mono";
|
||||
}
|
||||
@font-face {
|
||||
font-family: d2-1132014075-font-mono;
|
||||
src: url("data:application/font-woff;base64,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");
|
||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||
shape-rendering: geometricPrecision;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.connection {
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.blend {
|
||||
mix-blend-mode: multiply;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.d2-1132014075 .fill-N1{fill:#000410;}
|
||||
.d2-1132014075 .fill-N2{fill:#0000B8;}
|
||||
.d2-1132014075 .fill-N3{fill:#9499AB;}
|
||||
.d2-1132014075 .fill-N4{fill:#CFD2DD;}
|
||||
.d2-1132014075 .fill-N5{fill:#C3DEF3;}
|
||||
.d2-1132014075 .fill-N6{fill:#EEF1F8;}
|
||||
.d2-1132014075 .fill-N7{fill:#FFFFFF;}
|
||||
.d2-1132014075 .fill-B1{fill:#000410;}
|
||||
.d2-1132014075 .fill-B2{fill:#0000E4;}
|
||||
.d2-1132014075 .fill-B3{fill:#5AA4DC;}
|
||||
.d2-1132014075 .fill-B4{fill:#E7E9EE;}
|
||||
.d2-1132014075 .fill-B5{fill:#F5F6F9;}
|
||||
.d2-1132014075 .fill-B6{fill:#FFFFFF;}
|
||||
.d2-1132014075 .fill-AA2{fill:#008566;}
|
||||
.d2-1132014075 .fill-AA4{fill:#45BBA5;}
|
||||
.d2-1132014075 .fill-AA5{fill:#7ACCBD;}
|
||||
.d2-1132014075 .fill-AB4{fill:#F1C759;}
|
||||
.d2-1132014075 .fill-AB5{fill:#F9E088;}
|
||||
.d2-1132014075 .stroke-N1{stroke:#000410;}
|
||||
.d2-1132014075 .stroke-N2{stroke:#0000B8;}
|
||||
.d2-1132014075 .stroke-N3{stroke:#9499AB;}
|
||||
.d2-1132014075 .stroke-N4{stroke:#CFD2DD;}
|
||||
.d2-1132014075 .stroke-N5{stroke:#C3DEF3;}
|
||||
.d2-1132014075 .stroke-N6{stroke:#EEF1F8;}
|
||||
.d2-1132014075 .stroke-N7{stroke:#FFFFFF;}
|
||||
.d2-1132014075 .stroke-B1{stroke:#000410;}
|
||||
.d2-1132014075 .stroke-B2{stroke:#0000E4;}
|
||||
.d2-1132014075 .stroke-B3{stroke:#5AA4DC;}
|
||||
.d2-1132014075 .stroke-B4{stroke:#E7E9EE;}
|
||||
.d2-1132014075 .stroke-B5{stroke:#F5F6F9;}
|
||||
.d2-1132014075 .stroke-B6{stroke:#FFFFFF;}
|
||||
.d2-1132014075 .stroke-AA2{stroke:#008566;}
|
||||
.d2-1132014075 .stroke-AA4{stroke:#45BBA5;}
|
||||
.d2-1132014075 .stroke-AA5{stroke:#7ACCBD;}
|
||||
.d2-1132014075 .stroke-AB4{stroke:#F1C759;}
|
||||
.d2-1132014075 .stroke-AB5{stroke:#F9E088;}
|
||||
.d2-1132014075 .background-color-N1{background-color:#000410;}
|
||||
.d2-1132014075 .background-color-N2{background-color:#0000B8;}
|
||||
.d2-1132014075 .background-color-N3{background-color:#9499AB;}
|
||||
.d2-1132014075 .background-color-N4{background-color:#CFD2DD;}
|
||||
.d2-1132014075 .background-color-N5{background-color:#C3DEF3;}
|
||||
.d2-1132014075 .background-color-N6{background-color:#EEF1F8;}
|
||||
.d2-1132014075 .background-color-N7{background-color:#FFFFFF;}
|
||||
.d2-1132014075 .background-color-B1{background-color:#000410;}
|
||||
.d2-1132014075 .background-color-B2{background-color:#0000E4;}
|
||||
.d2-1132014075 .background-color-B3{background-color:#5AA4DC;}
|
||||
.d2-1132014075 .background-color-B4{background-color:#E7E9EE;}
|
||||
.d2-1132014075 .background-color-B5{background-color:#F5F6F9;}
|
||||
.d2-1132014075 .background-color-B6{background-color:#FFFFFF;}
|
||||
.d2-1132014075 .background-color-AA2{background-color:#008566;}
|
||||
.d2-1132014075 .background-color-AA4{background-color:#45BBA5;}
|
||||
.d2-1132014075 .background-color-AA5{background-color:#7ACCBD;}
|
||||
.d2-1132014075 .background-color-AB4{background-color:#F1C759;}
|
||||
.d2-1132014075 .background-color-AB5{background-color:#F9E088;}
|
||||
.d2-1132014075 .color-N1{color:#000410;}
|
||||
.d2-1132014075 .color-N2{color:#0000B8;}
|
||||
.d2-1132014075 .color-N3{color:#9499AB;}
|
||||
.d2-1132014075 .color-N4{color:#CFD2DD;}
|
||||
.d2-1132014075 .color-N5{color:#C3DEF3;}
|
||||
.d2-1132014075 .color-N6{color:#EEF1F8;}
|
||||
.d2-1132014075 .color-N7{color:#FFFFFF;}
|
||||
.d2-1132014075 .color-B1{color:#000410;}
|
||||
.d2-1132014075 .color-B2{color:#0000E4;}
|
||||
.d2-1132014075 .color-B3{color:#5AA4DC;}
|
||||
.d2-1132014075 .color-B4{color:#E7E9EE;}
|
||||
.d2-1132014075 .color-B5{color:#F5F6F9;}
|
||||
.d2-1132014075 .color-B6{color:#FFFFFF;}
|
||||
.d2-1132014075 .color-AA2{color:#008566;}
|
||||
.d2-1132014075 .color-AA4{color:#45BBA5;}
|
||||
.d2-1132014075 .color-AA5{color:#7ACCBD;}
|
||||
.d2-1132014075 .color-AB4{color:#F1C759;}
|
||||
.d2-1132014075 .color-AB5{color:#F9E088;}.appendix text.text{fill:#000410}.md{--color-fg-default:#000410;--color-fg-muted:#0000B8;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#000410;--color-border-muted:#0000E4;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0000E4;--color-accent-emphasis:#0000E4;--color-attention-subtle:#0000B8;--color-danger-fg:red;}.sketch-overlay-B1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B2{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-B3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-B4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-B6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AA2{fill:url(#streaks-dark);mix-blend-mode:overlay}.sketch-overlay-AA4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-AA5{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-AB4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-AB5{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N3{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N4{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N5{fill:url(#streaks-normal);mix-blend-mode:color-burn}.sketch-overlay-N6{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N7{fill:url(#streaks-bright);mix-blend-mode:darken}.light-code{display: block}.dark-code{display: none}]]></style><style type="text/css">.md em,
|
||||
.md dfn {
|
||||
font-family: "d2-1132014075-font-italic";
|
||||
}
|
||||
|
||||
.md b,
|
||||
.md strong {
|
||||
font-family: "d2-1132014075-font-bold";
|
||||
}
|
||||
|
||||
.md code,
|
||||
.md kbd,
|
||||
.md pre,
|
||||
.md samp {
|
||||
font-family: "d2-1132014075-font-mono";
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.md {
|
||||
tab-size: 4;
|
||||
}
|
||||
|
||||
/* variables are provided in d2renderers/d2svg/d2svg.go */
|
||||
|
||||
.md {
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
margin: 0;
|
||||
color: var(--color-fg-default);
|
||||
background-color: transparent; /* we don't want to define the background color */
|
||||
font-family: "d2-1132014075-font-regular";
|
||||
font-size: 16px;
|
||||
line-height: 1.5;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.md details,
|
||||
.md figcaption,
|
||||
.md figure {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.md summary {
|
||||
display: list-item;
|
||||
}
|
||||
|
||||
.md [hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.md a {
|
||||
background-color: transparent;
|
||||
color: var(--color-accent-fg);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.md a:active,
|
||||
.md a:hover {
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
.md abbr[title] {
|
||||
border-bottom: none;
|
||||
text-decoration: underline dotted;
|
||||
}
|
||||
|
||||
.md dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.md h1 {
|
||||
margin: 0.67em 0;
|
||||
padding-bottom: 0.3em;
|
||||
font-size: 2em;
|
||||
border-bottom: 1px solid var(--color-border-muted);
|
||||
}
|
||||
|
||||
.md mark {
|
||||
background-color: var(--color-attention-subtle);
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
.md small {
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
.md sub,
|
||||
.md sup {
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
position: relative;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
.md sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
|
||||
.md sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
|
||||
.md img {
|
||||
border-style: none;
|
||||
max-width: 100%;
|
||||
box-sizing: content-box;
|
||||
background-color: var(--color-canvas-default);
|
||||
}
|
||||
|
||||
.md figure {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
|
||||
.md hr {
|
||||
box-sizing: content-box;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
border-bottom: 1px solid var(--color-border-muted);
|
||||
height: 0.25em;
|
||||
padding: 0;
|
||||
margin: 24px 0;
|
||||
background-color: var(--color-border-default);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.md input {
|
||||
font: inherit;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
.md [type="button"],
|
||||
.md [type="reset"],
|
||||
.md [type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
.md [type="button"]::-moz-focus-inner,
|
||||
.md [type="reset"]::-moz-focus-inner,
|
||||
.md [type="submit"]::-moz-focus-inner {
|
||||
border-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.md [type="button"]:-moz-focusring,
|
||||
.md [type="reset"]:-moz-focusring,
|
||||
.md [type="submit"]:-moz-focusring {
|
||||
outline: 1px dotted ButtonText;
|
||||
}
|
||||
|
||||
.md [type="checkbox"],
|
||||
.md [type="radio"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.md [type="number"]::-webkit-inner-spin-button,
|
||||
.md [type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.md [type="search"] {
|
||||
-webkit-appearance: textfield;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
.md [type="search"]::-webkit-search-cancel-button,
|
||||
.md [type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
.md ::-webkit-input-placeholder {
|
||||
color: inherit;
|
||||
opacity: 0.54;
|
||||
}
|
||||
|
||||
.md ::-webkit-file-upload-button {
|
||||
-webkit-appearance: button;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.md a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.md hr::before {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.md hr::after {
|
||||
display: table;
|
||||
clear: both;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.md table {
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
display: block;
|
||||
width: max-content;
|
||||
max-width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.md td,
|
||||
.md th {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.md details summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.md details:not([open]) > *:not(summary) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.md kbd {
|
||||
display: inline-block;
|
||||
padding: 3px 5px;
|
||||
color: var(--color-fg-default);
|
||||
vertical-align: middle;
|
||||
background-color: var(--color-canvas-subtle);
|
||||
border: solid 1px var(--color-neutral-muted);
|
||||
border-bottom-color: var(--color-neutral-muted);
|
||||
border-radius: 6px;
|
||||
box-shadow: inset 0 -1px 0 var(--color-neutral-muted);
|
||||
}
|
||||
|
||||
.md h1,
|
||||
.md h2,
|
||||
.md h3,
|
||||
.md h4,
|
||||
.md h5,
|
||||
.md h6 {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 16px;
|
||||
font-weight: 400;
|
||||
line-height: 1.25;
|
||||
font-family: "font-semibold";
|
||||
}
|
||||
|
||||
.md h2 {
|
||||
padding-bottom: 0.3em;
|
||||
font-size: 1.5em;
|
||||
border-bottom: 1px solid var(--color-border-muted);
|
||||
}
|
||||
|
||||
.md h3 {
|
||||
font-size: 1.25em;
|
||||
}
|
||||
|
||||
.md h4 {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.md h5 {
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
.md h6 {
|
||||
font-size: 0.85em;
|
||||
color: var(--color-fg-muted);
|
||||
}
|
||||
|
||||
.md p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.md blockquote {
|
||||
margin: 0;
|
||||
padding: 0 1em;
|
||||
color: var(--color-fg-muted);
|
||||
border-left: 0.25em solid var(--color-border-default);
|
||||
}
|
||||
|
||||
.md ul,
|
||||
.md ol {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
padding-left: 2em;
|
||||
}
|
||||
|
||||
.md ol ol,
|
||||
.md ul ol {
|
||||
list-style-type: lower-roman;
|
||||
}
|
||||
|
||||
.md ul ul ol,
|
||||
.md ul ol ol,
|
||||
.md ol ul ol,
|
||||
.md ol ol ol {
|
||||
list-style-type: lower-alpha;
|
||||
}
|
||||
|
||||
.md dd {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.md pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
word-wrap: normal;
|
||||
}
|
||||
|
||||
.md ::placeholder {
|
||||
color: var(--color-fg-subtle);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.md input::-webkit-outer-spin-button,
|
||||
.md input::-webkit-inner-spin-button {
|
||||
margin: 0;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
}
|
||||
|
||||
.md::before {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.md::after {
|
||||
display: table;
|
||||
clear: both;
|
||||
content: "";
|
||||
}
|
||||
|
||||
.md > *:first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
.md > *:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.md a:not([href]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.md .absent {
|
||||
color: var(--color-danger-fg);
|
||||
}
|
||||
|
||||
.md .anchor {
|
||||
float: left;
|
||||
padding-right: 4px;
|
||||
margin-left: -20px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.md .anchor:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.md p,
|
||||
.md blockquote,
|
||||
.md ul,
|
||||
.md ol,
|
||||
.md dl,
|
||||
.md table,
|
||||
.md pre,
|
||||
.md details {
|
||||
margin-top: 0;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.md blockquote > :first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.md blockquote > :last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.md sup > a::before {
|
||||
content: "[";
|
||||
}
|
||||
|
||||
.md sup > a::after {
|
||||
content: "]";
|
||||
}
|
||||
|
||||
.md h1:hover .anchor,
|
||||
.md h2:hover .anchor,
|
||||
.md h3:hover .anchor,
|
||||
.md h4:hover .anchor,
|
||||
.md h5:hover .anchor,
|
||||
.md h6:hover .anchor {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.md h1 tt,
|
||||
.md h1 code,
|
||||
.md h2 tt,
|
||||
.md h2 code,
|
||||
.md h3 tt,
|
||||
.md h3 code,
|
||||
.md h4 tt,
|
||||
.md h4 code,
|
||||
.md h5 tt,
|
||||
.md h5 code,
|
||||
.md h6 tt,
|
||||
.md h6 code {
|
||||
padding: 0 0.2em;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.md ul.no-list,
|
||||
.md ol.no-list {
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.md ol[type="1"] {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
.md ol[type="a"] {
|
||||
list-style-type: lower-alpha;
|
||||
}
|
||||
|
||||
.md ol[type="i"] {
|
||||
list-style-type: lower-roman;
|
||||
}
|
||||
|
||||
.md div > ol:not([type]) {
|
||||
list-style-type: decimal;
|
||||
}
|
||||
|
||||
.md ul ul,
|
||||
.md ul ol,
|
||||
.md ol ol,
|
||||
.md ol ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.md li > p {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.md li + li {
|
||||
margin-top: 0.25em;
|
||||
}
|
||||
|
||||
.md dl {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.md dl dt {
|
||||
padding: 0;
|
||||
margin-top: 16px;
|
||||
font-size: 1em;
|
||||
font-style: italic;
|
||||
font-family: "font-semibold";
|
||||
}
|
||||
|
||||
.md dl dd {
|
||||
padding: 0 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.md table th {
|
||||
font-family: "font-semibold";
|
||||
}
|
||||
|
||||
.md table th,
|
||||
.md table td {
|
||||
padding: 6px 13px;
|
||||
border: 1px solid var(--color-border-default);
|
||||
}
|
||||
|
||||
.md table tr {
|
||||
background-color: var(--color-canvas-default);
|
||||
border-top: 1px solid var(--color-border-muted);
|
||||
}
|
||||
|
||||
.md table tr:nth-child(2n) {
|
||||
background-color: var(--color-canvas-subtle);
|
||||
}
|
||||
|
||||
.md table img {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.md img[align="right"] {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
.md img[align="left"] {
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.md span.frame {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.md span.frame > span {
|
||||
display: block;
|
||||
float: left;
|
||||
width: auto;
|
||||
padding: 7px;
|
||||
margin: 13px 0 0;
|
||||
overflow: hidden;
|
||||
border: 1px solid var(--color-border-default);
|
||||
}
|
||||
|
||||
.md span.frame span img {
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.md span.frame span span {
|
||||
display: block;
|
||||
padding: 5px 0 0;
|
||||
clear: both;
|
||||
color: var(--color-fg-default);
|
||||
}
|
||||
|
||||
.md span.align-center {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.md span.align-center > span {
|
||||
display: block;
|
||||
margin: 13px auto 0;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.md span.align-center span img {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.md span.align-right {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.md span.align-right > span {
|
||||
display: block;
|
||||
margin: 13px 0 0;
|
||||
overflow: hidden;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.md span.align-right span img {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.md span.float-left {
|
||||
display: block;
|
||||
float: left;
|
||||
margin-right: 13px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.md span.float-left span {
|
||||
margin: 13px 0 0;
|
||||
}
|
||||
|
||||
.md span.float-right {
|
||||
display: block;
|
||||
float: right;
|
||||
margin-left: 13px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.md span.float-right > span {
|
||||
display: block;
|
||||
margin: 13px auto 0;
|
||||
overflow: hidden;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.md code,
|
||||
.md tt {
|
||||
padding: 0.2em 0.4em;
|
||||
margin: 0;
|
||||
font-size: 85%;
|
||||
background-color: var(--color-neutral-muted);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.md code br,
|
||||
.md tt br {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.md del code {
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
.md pre code {
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
.md pre > code {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
word-break: normal;
|
||||
white-space: pre;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.md .highlight {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.md .highlight pre {
|
||||
margin-bottom: 0;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.md .highlight pre,
|
||||
.md pre {
|
||||
padding: 16px;
|
||||
overflow: auto;
|
||||
font-size: 85%;
|
||||
line-height: 1.45;
|
||||
background-color: var(--color-canvas-subtle);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.md pre code,
|
||||
.md pre tt {
|
||||
display: inline;
|
||||
max-width: auto;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow: visible;
|
||||
line-height: inherit;
|
||||
word-wrap: normal;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.md .csv-data td,
|
||||
.md .csv-data th {
|
||||
padding: 5px;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.md .csv-data .blob-num {
|
||||
padding: 10px 8px 9px;
|
||||
text-align: right;
|
||||
background: var(--color-canvas-default);
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.md .csv-data tr {
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.md .csv-data th {
|
||||
font-family: "font-semibold";
|
||||
background: var(--color-canvas-subtle);
|
||||
border-top: 0;
|
||||
}
|
||||
|
||||
.md .footnotes {
|
||||
font-size: 12px;
|
||||
color: var(--color-fg-muted);
|
||||
border-top: 1px solid var(--color-border-default);
|
||||
}
|
||||
|
||||
.md .footnotes ol {
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.md .footnotes li {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.md .footnotes li:target::before {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
right: -8px;
|
||||
bottom: -8px;
|
||||
left: -24px;
|
||||
pointer-events: none;
|
||||
content: "";
|
||||
border: 2px solid var(--color-accent-emphasis);
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.md .footnotes li:target {
|
||||
color: var(--color-fg-default);
|
||||
}
|
||||
|
||||
.md .task-list-item {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.md .task-list-item label {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.md .task-list-item.enabled label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.md .task-list-item + .task-list-item {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.md .task-list-item .handle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.md .task-list-item-checkbox {
|
||||
margin: 0 0.2em 0.25em -1.6em;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.md .contains-task-list:dir(rtl) .task-list-item-checkbox {
|
||||
margin: 0 -1.6em 0.25em 0.2em;
|
||||
}
|
||||
</style><style type="text/css"><![CDATA[@keyframes d2Transition-d2-1132014075-0 {
|
||||
0%, 0.000000% {
|
||||
opacity: 0;
|
||||
}
|
||||
0.000000%, 24.982143% {
|
||||
opacity: 1;
|
||||
}
|
||||
25.000000%, 100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}@keyframes d2Transition-d2-1132014075-1 {
|
||||
0%, 24.982143% {
|
||||
opacity: 0;
|
||||
}
|
||||
25.000000%, 49.982143% {
|
||||
opacity: 1;
|
||||
}
|
||||
50.000000%, 100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}@keyframes d2Transition-d2-1132014075-2 {
|
||||
0%, 49.982143% {
|
||||
opacity: 0;
|
||||
}
|
||||
50.000000%, 74.982143% {
|
||||
opacity: 1;
|
||||
}
|
||||
75.000000%, 100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}@keyframes d2Transition-d2-1132014075-3 {
|
||||
0%, 74.982143% {
|
||||
opacity: 0;
|
||||
}
|
||||
75.000000%, 100.000000% {
|
||||
opacity: 1;
|
||||
}
|
||||
}]]></style><g style="animation: d2Transition-d2-1132014075-0 5600ms infinite" class="d2-1132014075" width="492" height="247" viewBox="-246 -166 492 247"><rect x="-246.000000" y="-166.000000" width="492.000000" height="247.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><g id=""Chicken's plan""><g class="shape" ></g><text x="0.000000" y="-30.000000" class="text-mono fill-N1" style="text-anchor:middle;font-size:35px">CHICKEN'S PLAN</text></g><mask id="d2-1132014075" maskUnits="userSpaceOnUse" x="-246" y="-166" width="492" height="247">
|
||||
<rect x="-246" y="-166" width="492" height="247" fill="white"></rect>
|
||||
<rect x="-145.000000" y="-65.000000" width="290" height="45" fill="rgba(0,0,0,0.75)"></rect>
|
||||
</mask></g><g style="animation: d2Transition-d2-1132014075-1 5600ms infinite" class="d2-1132014075" width="492" height="333" viewBox="-160 -166 492 333"><rect x="-160.000000" y="-166.000000" width="492.000000" height="333.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><g id="Approach road"><g class="shape" ><rect x="0.000000" y="0.000000" width="171.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="85.500000" y="38.500000" class="text-mono fill-N1" style="text-anchor:middle;font-size:16px">APPROACH ROAD</text></g><g id=""Chicken's plan""><g class="shape" ></g><text x="86.000000" y="-30.000000" class="text-mono fill-N1" style="text-anchor:middle;font-size:35px">CHICKEN'S PLAN</text></g><mask id="d2-3467174386" maskUnits="userSpaceOnUse" x="-160" y="-166" width="492" height="333">
|
||||
<rect x="-160" y="-166" width="492" height="333" fill="white"></rect>
|
||||
<rect x="22.500000" y="22.500000" width="126" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||
<rect x="-59.000000" y="-65.000000" width="290" height="45" fill="rgba(0,0,0,0.75)"></rect>
|
||||
</mask></g><g style="animation: d2Transition-d2-1132014075-2 5600ms infinite" class="d2-1132014075" width="492" height="499" viewBox="-160 -166 492 499"><rect x="-160.000000" y="-166.000000" width="492.000000" height="499.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><g id="Approach road"><g class="shape" ><rect x="0.000000" y="0.000000" width="171.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="85.500000" y="38.500000" class="text-mono fill-N1" style="text-anchor:middle;font-size:16px">APPROACH ROAD</text></g><g id="Cross road"><g class="shape" ><rect x="15.000000" y="166.000000" width="142.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="86.000000" y="204.500000" class="text-mono fill-N1" style="text-anchor:middle;font-size:16px">CROSS ROAD</text></g><g id=""Chicken's plan""><g class="shape" ></g><text x="86.000000" y="-30.000000" class="text-mono fill-N1" style="text-anchor:middle;font-size:35px">CHICKEN'S PLAN</text></g><g id="(Approach road -> Cross road)[0]"><marker id="mk-3488378134" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" class="connection fill-B1" stroke-width="2" /> </marker><path d="M 85.500000 68.000000 C 85.500000 106.000000 85.500000 126.000000 85.500000 162.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-2945557013)" /></g><mask id="d2-2945557013" maskUnits="userSpaceOnUse" x="-160" y="-166" width="492" height="499">
|
||||
<rect x="-160" y="-166" width="492" height="499" fill="white"></rect>
|
||||
<rect x="22.500000" y="22.500000" width="126" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||
<rect x="37.500000" y="188.500000" width="97" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||
<rect x="-59.000000" y="-65.000000" width="290" height="45" fill="rgba(0,0,0,0.75)"></rect>
|
||||
</mask></g><g style="animation: d2Transition-d2-1132014075-3 5600ms infinite" class="d2-1132014075" width="492" height="665" viewBox="-132 -166 492 665"><rect x="-132.000000" y="-166.000000" width="492.000000" height="665.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><g id="Approach road"><g class="shape" ><rect x="29.000000" y="0.000000" width="171.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="114.500000" y="38.500000" class="text-mono fill-N1" style="text-anchor:middle;font-size:16px">APPROACH ROAD</text></g><g id="Cross road"><g class="shape" ><rect x="43.000000" y="166.000000" width="142.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="114.000000" y="204.500000" class="text-mono fill-N1" style="text-anchor:middle;font-size:16px">CROSS ROAD</text></g><g id="Make you wonder why"><g class="shape" ><rect x="0.000000" y="332.000000" width="228.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="114.000000" y="370.500000" class="text-mono fill-N1" style="text-anchor:middle;font-size:16px">MAKE YOU WONDER WHY</text></g><g id=""Chicken's plan""><g class="shape" ></g><text x="114.000000" y="-30.000000" class="text-mono fill-N1" style="text-anchor:middle;font-size:35px">CHICKEN'S PLAN</text></g><g id="(Approach road -> Cross road)[0]"><marker id="mk-3488378134" markerWidth="10.000000" markerHeight="12.000000" refX="7.000000" refY="6.000000" viewBox="0.000000 0.000000 10.000000 12.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="0.000000,0.000000 10.000000,6.000000 0.000000,12.000000" class="connection fill-B1" stroke-width="2" /> </marker><path d="M 114.000000 68.000000 C 114.000000 106.000000 114.000000 126.000000 114.000000 162.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-2467861000)" /></g><g id="(Cross road -> Make you wonder why)[0]"><path d="M 114.000000 234.000000 C 114.000000 272.000000 114.000000 292.000000 114.000000 328.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-2467861000)" /></g><mask id="d2-2467861000" maskUnits="userSpaceOnUse" x="-132" y="-166" width="492" height="665">
|
||||
<rect x="-132" y="-166" width="492" height="665" fill="white"></rect>
|
||||
<rect x="51.500000" y="22.500000" width="126" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||
<rect x="65.500000" y="188.500000" width="97" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||
<rect x="22.500000" y="354.500000" width="183" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||
<rect x="-31.000000" y="-65.000000" width="290" height="45" fill="rgba(0,0,0,0.75)"></rect>
|
||||
</mask></g></svg></svg>
|
||||
|
After Width: | Height: | Size: 28 KiB |
117
e2etests-cli/testdata/TestCLI_E2E/vars-config.exp.svg
vendored
Normal file
117
e2etests-cli/testdata/TestCLI_E2E/vars-config.exp.svg
vendored
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 77 KiB |
|
|
@ -15,6 +15,7 @@ import (
|
|||
|
||||
"oss.terrastruct.com/util-go/assert"
|
||||
"oss.terrastruct.com/util-go/diff"
|
||||
"oss.terrastruct.com/util-go/go2"
|
||||
|
||||
"oss.terrastruct.com/d2/d2compiler"
|
||||
"oss.terrastruct.com/d2/d2graph"
|
||||
|
|
@ -87,7 +88,7 @@ type testCase struct {
|
|||
dagreFeatureError string
|
||||
elkFeatureError string
|
||||
expErr string
|
||||
themeID int64
|
||||
themeID *int64
|
||||
}
|
||||
|
||||
func runa(t *testing.T, tcs []testCase) {
|
||||
|
|
@ -109,7 +110,7 @@ func runa(t *testing.T, tcs []testCase) {
|
|||
func serde(t *testing.T, tc testCase, ruler *textmeasure.Ruler) {
|
||||
ctx := context.Background()
|
||||
ctx = log.WithTB(ctx, t, nil)
|
||||
g, err := d2compiler.Compile("", strings.NewReader(tc.script), &d2compiler.CompileOptions{
|
||||
g, _, err := d2compiler.Compile("", strings.NewReader(tc.script), &d2compiler.CompileOptions{
|
||||
UTF16: false,
|
||||
})
|
||||
trequire.Nil(t, err)
|
||||
|
|
@ -146,28 +147,39 @@ func run(t *testing.T, tc testCase) {
|
|||
layoutsTested = append(layoutsTested, "elk")
|
||||
}
|
||||
|
||||
layoutResolver := func(engine string) (d2graph.LayoutGraph, error) {
|
||||
if strings.EqualFold(engine, "elk") {
|
||||
return d2elklayout.DefaultLayout, nil
|
||||
}
|
||||
return d2dagrelayout.DefaultLayout, nil
|
||||
}
|
||||
|
||||
for _, layoutName := range layoutsTested {
|
||||
var layout func(context.Context, *d2graph.Graph) error
|
||||
var plugin d2plugin.Plugin
|
||||
if layoutName == "dagre" {
|
||||
layout = d2dagrelayout.DefaultLayout
|
||||
plugin = &d2plugin.DagrePlugin
|
||||
} else if layoutName == "elk" {
|
||||
// If measured texts exists, we are specifically exercising text measurements, no need to run on both layouts
|
||||
if tc.mtexts != nil {
|
||||
continue
|
||||
}
|
||||
layout = d2elklayout.DefaultLayout
|
||||
plugin = &d2plugin.ELKPlugin
|
||||
}
|
||||
|
||||
diagram, g, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
|
||||
compileOpts := &d2lib.CompileOptions{
|
||||
Ruler: ruler,
|
||||
MeasuredTexts: tc.mtexts,
|
||||
Layout: layout,
|
||||
Layout: go2.Pointer(layoutName),
|
||||
LayoutResolver: layoutResolver,
|
||||
}
|
||||
renderOpts := &d2svg.RenderOpts{
|
||||
Pad: go2.Pointer(int64(0)),
|
||||
ThemeID: tc.themeID,
|
||||
})
|
||||
// To compare deltas at a fixed scale
|
||||
// Scale: go2.Pointer(1.),
|
||||
}
|
||||
|
||||
diagram, g, err := d2lib.Compile(ctx, tc.script, compileOpts, renderOpts)
|
||||
if tc.expErr != "" {
|
||||
assert.Error(t, err)
|
||||
assert.ErrorString(t, err, tc.expErr)
|
||||
|
|
@ -205,12 +217,6 @@ func run(t *testing.T, tc testCase) {
|
|||
dataPath := filepath.Join("testdata", strings.TrimPrefix(t.Name(), "TestE2E/"), layoutName)
|
||||
pathGotSVG := filepath.Join(dataPath, "sketch.got.svg")
|
||||
|
||||
renderOpts := &d2svg.RenderOpts{
|
||||
Pad: 0,
|
||||
ThemeID: tc.themeID,
|
||||
// To compare deltas at a fixed scale
|
||||
// Scale: go2.Pointer(1.),
|
||||
}
|
||||
if len(diagram.Layers) > 0 || len(diagram.Scenarios) > 0 || len(diagram.Steps) > 0 {
|
||||
masterID, err := diagram.HashID()
|
||||
assert.Success(t, err)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ func testThemes(t *testing.T) {
|
|||
tcs := []testCase{
|
||||
{
|
||||
name: "dark terrastruct flagship",
|
||||
themeID: d2themescatalog.DarkFlagshipTerrastruct.ID,
|
||||
themeID: &d2themescatalog.DarkFlagshipTerrastruct.ID,
|
||||
script: `
|
||||
network: {
|
||||
cell tower: {
|
||||
|
|
@ -118,7 +118,7 @@ ex: |tex
|
|||
},
|
||||
{
|
||||
name: "terminal",
|
||||
themeID: d2themescatalog.Terminal.ID,
|
||||
themeID: &d2themescatalog.Terminal.ID,
|
||||
script: `
|
||||
network: {
|
||||
cell tower: {
|
||||
|
|
@ -225,7 +225,7 @@ ex: |tex
|
|||
},
|
||||
{
|
||||
name: "terminal_grayscale",
|
||||
themeID: d2themescatalog.TerminalGrayscale.ID,
|
||||
themeID: &d2themescatalog.TerminalGrayscale.ID,
|
||||
script: `
|
||||
network: {
|
||||
cell tower: {
|
||||
|
|
@ -279,7 +279,7 @@ network.data processor -> api server
|
|||
},
|
||||
{
|
||||
name: "origami",
|
||||
themeID: d2themescatalog.Origami.ID,
|
||||
themeID: &d2themescatalog.Origami.ID,
|
||||
script: `
|
||||
network: 通信網 {
|
||||
cell tower: {
|
||||
|
|
|
|||
291
testdata/d2compiler/TestCompile2/vars/config/basic.exp.json
generated
vendored
Normal file
291
testdata/d2compiler/TestCompile2/vars/config/basic.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,291 @@
|
|||
{
|
||||
"graph": {
|
||||
"name": "",
|
||||
"isFolderOnly": false,
|
||||
"ast": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,0:0:0-8:0:54",
|
||||
"nodes": [
|
||||
{
|
||||
"map_key": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,1:0:1-5:1:45",
|
||||
"key": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,1:0:1-1:4:5",
|
||||
"path": [
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,1:0:1-1:4:5",
|
||||
"value": [
|
||||
{
|
||||
"string": "vars",
|
||||
"raw_string": "vars"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"primary": {},
|
||||
"value": {
|
||||
"map": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,1:6:7-5:1:45",
|
||||
"nodes": [
|
||||
{
|
||||
"map_key": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,2:1:10-4:3:43",
|
||||
"key": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,2:1:10-2:10:19",
|
||||
"path": [
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,2:1:10-2:10:19",
|
||||
"value": [
|
||||
{
|
||||
"string": "d2-config",
|
||||
"raw_string": "d2-config"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"primary": {},
|
||||
"value": {
|
||||
"map": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,2:12:21-4:3:43",
|
||||
"nodes": [
|
||||
{
|
||||
"map_key": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,3:4:27-3:16:39",
|
||||
"key": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,3:4:27-3:10:33",
|
||||
"path": [
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,3:4:27-3:10:33",
|
||||
"value": [
|
||||
{
|
||||
"string": "sketch",
|
||||
"raw_string": "sketch"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"primary": {},
|
||||
"value": {
|
||||
"boolean": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,3:12:35-3:16:39",
|
||||
"value": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"map_key": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,7:0:47-7:6:53",
|
||||
"edges": [
|
||||
{
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,7:0:47-7:6:53",
|
||||
"src": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,7:0:47-7:1:48",
|
||||
"path": [
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,7:0:47-7:1:48",
|
||||
"value": [
|
||||
{
|
||||
"string": "x",
|
||||
"raw_string": "x"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"src_arrow": "",
|
||||
"dst": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,7:5:52-7:6:53",
|
||||
"path": [
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,7:5:52-7:6:53",
|
||||
"value": [
|
||||
{
|
||||
"string": "y",
|
||||
"raw_string": "y"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"dst_arrow": ">"
|
||||
}
|
||||
],
|
||||
"primary": {},
|
||||
"value": {}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"root": {
|
||||
"id": "",
|
||||
"id_val": "",
|
||||
"attributes": {
|
||||
"label": {
|
||||
"value": ""
|
||||
},
|
||||
"labelDimensions": {
|
||||
"width": 0,
|
||||
"height": 0
|
||||
},
|
||||
"style": {},
|
||||
"near_key": null,
|
||||
"shape": {
|
||||
"value": ""
|
||||
},
|
||||
"direction": {
|
||||
"value": ""
|
||||
},
|
||||
"constraint": null
|
||||
},
|
||||
"zIndex": 0
|
||||
},
|
||||
"edges": [
|
||||
{
|
||||
"index": 0,
|
||||
"isCurve": false,
|
||||
"src_arrow": false,
|
||||
"dst_arrow": true,
|
||||
"references": [
|
||||
{
|
||||
"map_key_edge_index": 0
|
||||
}
|
||||
],
|
||||
"attributes": {
|
||||
"label": {
|
||||
"value": ""
|
||||
},
|
||||
"labelDimensions": {
|
||||
"width": 0,
|
||||
"height": 0
|
||||
},
|
||||
"style": {},
|
||||
"near_key": null,
|
||||
"shape": {
|
||||
"value": ""
|
||||
},
|
||||
"direction": {
|
||||
"value": ""
|
||||
},
|
||||
"constraint": null
|
||||
},
|
||||
"zIndex": 0
|
||||
}
|
||||
],
|
||||
"objects": [
|
||||
{
|
||||
"id": "x",
|
||||
"id_val": "x",
|
||||
"references": [
|
||||
{
|
||||
"key": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,7:0:47-7:1:48",
|
||||
"path": [
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,7:0:47-7:1:48",
|
||||
"value": [
|
||||
{
|
||||
"string": "x",
|
||||
"raw_string": "x"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"key_path_index": 0,
|
||||
"map_key_edge_index": 0
|
||||
}
|
||||
],
|
||||
"attributes": {
|
||||
"label": {
|
||||
"value": "x"
|
||||
},
|
||||
"labelDimensions": {
|
||||
"width": 0,
|
||||
"height": 0
|
||||
},
|
||||
"style": {},
|
||||
"near_key": null,
|
||||
"shape": {
|
||||
"value": "rectangle"
|
||||
},
|
||||
"direction": {
|
||||
"value": ""
|
||||
},
|
||||
"constraint": null
|
||||
},
|
||||
"zIndex": 0
|
||||
},
|
||||
{
|
||||
"id": "y",
|
||||
"id_val": "y",
|
||||
"references": [
|
||||
{
|
||||
"key": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,7:5:52-7:6:53",
|
||||
"path": [
|
||||
{
|
||||
"unquoted_string": {
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/basic.d2,7:5:52-7:6:53",
|
||||
"value": [
|
||||
{
|
||||
"string": "y",
|
||||
"raw_string": "y"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"key_path_index": 0,
|
||||
"map_key_edge_index": 0
|
||||
}
|
||||
],
|
||||
"attributes": {
|
||||
"label": {
|
||||
"value": "y"
|
||||
},
|
||||
"labelDimensions": {
|
||||
"width": 0,
|
||||
"height": 0
|
||||
},
|
||||
"style": {},
|
||||
"near_key": null,
|
||||
"shape": {
|
||||
"value": "rectangle"
|
||||
},
|
||||
"direction": {
|
||||
"value": ""
|
||||
},
|
||||
"constraint": null
|
||||
},
|
||||
"zIndex": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"err": null
|
||||
}
|
||||
11
testdata/d2compiler/TestCompile2/vars/config/invalid.exp.json
generated
vendored
Normal file
11
testdata/d2compiler/TestCompile2/vars/config/invalid.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"graph": null,
|
||||
"err": {
|
||||
"errs": [
|
||||
{
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/invalid.d2,3:4:27-3:10:33",
|
||||
"errmsg": "d2/testdata/d2compiler/TestCompile2/vars/config/invalid.d2:4:5: expected a boolean for \"sketch\", got \"lol\""
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
11
testdata/d2compiler/TestCompile2/vars/config/not-root.exp.json
generated
vendored
Normal file
11
testdata/d2compiler/TestCompile2/vars/config/not-root.exp.json
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"graph": null,
|
||||
"err": {
|
||||
"errs": [
|
||||
{
|
||||
"range": "d2/testdata/d2compiler/TestCompile2/vars/config/not-root.d2,3:3:19-3:12:28",
|
||||
"errmsg": "d2/testdata/d2compiler/TestCompile2/vars/config/not-root.d2:4:4: \"d2-config\" can only appear at root vars"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue