Merge branch 'master' of https://github.com/BarryNolte/d2
2
.github/workflows/daily.yml
vendored
|
|
@ -16,7 +16,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
go-version-file: ./go.mod
|
go-version-file: ./go.mod
|
||||||
cache: true
|
cache: true
|
||||||
- run: COLOR=1 CI_FORCE=1 ./make.sh all race
|
- run: DAILY=1 COLOR=1 CI_FORCE=1 ./make.sh all race
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets._GITHUB_TOKEN }}
|
||||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,5 @@
|
||||||
#### Features 🚀
|
#### Features 🚀
|
||||||
|
|
||||||
- Configure timeout value with D2_TIMEOUT env var [#1392](https://github.com/terrastruct/d2/pull/1392)
|
|
||||||
- Scale renders and disable fit to screen with `--scale` flag [#1413](https://github.com/terrastruct/d2/pull/1413)
|
|
||||||
- `null` keyword can be used to un-declare. See [docs](https://d2lang.com/tour/TODO) [#1446](https://github.com/terrastruct/d2/pull/1446)
|
|
||||||
|
|
||||||
#### Improvements 🧹
|
#### Improvements 🧹
|
||||||
|
|
||||||
- Display version on CLI help invocation [#1400](https://github.com/terrastruct/d2/pull/1400)
|
|
||||||
- Improved readability of connection labels when they overlap another connection [#447](https://github.com/terrastruct/d2/pull/447)
|
|
||||||
- Error message when `shape` is given a composite [#1415](https://github.com/terrastruct/d2/pull/1415)
|
|
||||||
- Improved rendering and text measurement for code shapes [#1425](https://github.com/terrastruct/d2/pull/1425)
|
|
||||||
- The autoformatter moves board declarations to the bottom of its scope [#1424](https://github.com/terrastruct/d2/pull/1424)
|
|
||||||
- All font styles in sketch mode use a consistent font-family [#1463](https://github.com/terrastruct/d2/pull/1463)
|
|
||||||
- Tooltip and link icons are now positioned on shape border [#1466](https://github.com/terrastruct/d2/pull/1466)
|
|
||||||
- Tooltip and link icons are always rendered over shapes [#1467](https://github.com/terrastruct/d2/pull/1467)
|
|
||||||
|
|
||||||
#### Bugfixes ⛑️
|
#### Bugfixes ⛑️
|
||||||
|
|
||||||
- Fixes edge case in compiler using dots in quotes [#1401](https://github.com/terrastruct/d2/pull/1401)
|
|
||||||
- Fixes grid label font size for TALA [#1412](https://github.com/terrastruct/d2/pull/1412)
|
|
||||||
|
|
|
||||||
54
ci/release/changelogs/v0.6.0.md
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
D2 v0.6 introduces variable substitutions and globs. These two were the last of the features planned in the initial designs for D2, and v1 is now very close!
|
||||||
|
|
||||||
|
The power of variables and globs in a programming language need no introduction, so here's two minimal examples to get started:
|
||||||
|
|
||||||
|
**Variables**:
|
||||||
|
```d2
|
||||||
|
vars: {
|
||||||
|
color: aquamarine
|
||||||
|
}
|
||||||
|
|
||||||
|
x.style.fill: ${color}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Globs**:
|
||||||
|
```d2
|
||||||
|
x
|
||||||
|
y
|
||||||
|
z
|
||||||
|
|
||||||
|
*.style.fill: aquamarine
|
||||||
|
```
|
||||||
|
|
||||||
|
Both are live on [D2 Playground](https://play.d2lang.com) so give it a try! Looking forward to your issues and iterating
|
||||||
|
|
||||||
|
Layout capability also takes a subtle but important step forward: you can now customize the position of labels and icons.
|
||||||
|
|
||||||
|
#### Features 🚀
|
||||||
|
|
||||||
|
- Variables and substitutions are implemented. See [docs](https://d2lang.com/tour/vars). [#1473](https://github.com/terrastruct/d2/pull/1473)
|
||||||
|
- Configure timeout value with D2_TIMEOUT env var [#1392](https://github.com/terrastruct/d2/pull/1392)
|
||||||
|
- Scale renders and disable fit to screen with `--scale` flag [#1413](https://github.com/terrastruct/d2/pull/1413)
|
||||||
|
- `null` keyword can be used to un-declare. See [docs](https://d2lang.com/tour/overrides#null) [#1446](https://github.com/terrastruct/d2/pull/1446)
|
||||||
|
- Develop multi-board diagrams in watch mode (links to layers/scenarios/steps work in `--watch`) [#1503](https://github.com/terrastruct/d2/pull/1503)
|
||||||
|
- Glob patterns have been implemented. See [docs](https://d2lang.com/tour/globs). [#1479](https://github.com/terrastruct/d2/pull/1479)
|
||||||
|
- Ampersand filters have been implemented. See [docs](https://d2lang.com/tour/filters). [#1509](https://github.com/terrastruct/d2/pull/1509)
|
||||||
|
|
||||||
|
#### Improvements 🧹
|
||||||
|
|
||||||
|
- Display version on CLI help invocation [#1400](https://github.com/terrastruct/d2/pull/1400)
|
||||||
|
- Improved readability of connection labels when they overlap another connection [#447](https://github.com/terrastruct/d2/pull/447)
|
||||||
|
- Error message when `shape` is given a composite [#1415](https://github.com/terrastruct/d2/pull/1415)
|
||||||
|
- Improved rendering and text measurement for code shapes [#1425](https://github.com/terrastruct/d2/pull/1425)
|
||||||
|
- The autoformatter moves board declarations to the bottom of its scope [#1424](https://github.com/terrastruct/d2/pull/1424)
|
||||||
|
- All font styles in sketch mode use a consistent font-family [#1463](https://github.com/terrastruct/d2/pull/1463)
|
||||||
|
- Tooltip and link icons are positioned on shape border [#1466](https://github.com/terrastruct/d2/pull/1466)
|
||||||
|
- Tooltip and link icons are always rendered over shapes [#1467](https://github.com/terrastruct/d2/pull/1467)
|
||||||
|
- Boards with no objects are considered folders [#1504](https://github.com/terrastruct/d2/pull/1504)
|
||||||
|
- `DEBUG` environment variable ignored if set incorrectly [#1505](https://github.com/terrastruct/d2/pull/1505)
|
||||||
|
|
||||||
|
#### Bugfixes ⛑️
|
||||||
|
|
||||||
|
- Fixes edge case in compiler using dots in quotes [#1401](https://github.com/terrastruct/d2/pull/1401)
|
||||||
|
- Fixes grid label font size for TALA [#1412](https://github.com/terrastruct/d2/pull/1412)
|
||||||
|
- Fixes person shape label positioning with `multiple` or `3d` [#1478](https://github.com/terrastruct/d2/pull/1478)
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# https://hub.docker.com/repository/docker/terrastruct/d2
|
# https://hub.docker.com/repository/docker/terrastruct/d2
|
||||||
FROM debian:latest
|
FROM ubuntu:latest
|
||||||
|
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -500,6 +500,19 @@ type Number struct {
|
||||||
type UnquotedString struct {
|
type UnquotedString struct {
|
||||||
Range Range `json:"range"`
|
Range Range `json:"range"`
|
||||||
Value []InterpolationBox `json:"value"`
|
Value []InterpolationBox `json:"value"`
|
||||||
|
// Pattern holds the parsed glob pattern if in a key and the unquoted string represents a valid pattern.
|
||||||
|
Pattern []string `json:"pattern,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UnquotedString) Coalesce() {
|
||||||
|
var b strings.Builder
|
||||||
|
for _, box := range s.Value {
|
||||||
|
if box.String == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
b.WriteString(*box.String)
|
||||||
|
}
|
||||||
|
s.SetString(b.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func FlatUnquotedString(s string) *UnquotedString {
|
func FlatUnquotedString(s string) *UnquotedString {
|
||||||
|
|
@ -513,6 +526,17 @@ type DoubleQuotedString struct {
|
||||||
Value []InterpolationBox `json:"value"`
|
Value []InterpolationBox `json:"value"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DoubleQuotedString) Coalesce() {
|
||||||
|
var b strings.Builder
|
||||||
|
for _, box := range s.Value {
|
||||||
|
if box.String == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
b.WriteString(*box.String)
|
||||||
|
}
|
||||||
|
s.SetString(b.String())
|
||||||
|
}
|
||||||
|
|
||||||
func FlatDoubleQuotedString(s string) *DoubleQuotedString {
|
func FlatDoubleQuotedString(s string) *DoubleQuotedString {
|
||||||
return &DoubleQuotedString{
|
return &DoubleQuotedString{
|
||||||
Value: []InterpolationBox{{String: &s}},
|
Value: []InterpolationBox{{String: &s}},
|
||||||
|
|
@ -586,7 +610,7 @@ func (m *Map) IsFileMap() bool {
|
||||||
type Key struct {
|
type Key struct {
|
||||||
Range Range `json:"range"`
|
Range Range `json:"range"`
|
||||||
|
|
||||||
// Indicates this MapKey is an override selector.
|
// Indicates this MapKey is a filter selector.
|
||||||
Ampersand bool `json:"ampersand,omitempty"`
|
Ampersand bool `json:"ampersand,omitempty"`
|
||||||
|
|
||||||
// At least one of Key and Edges will be set but all four can also be set.
|
// At least one of Key and Edges will be set but all four can also be set.
|
||||||
|
|
@ -696,6 +720,19 @@ func (mk *Key) SetScalar(scalar ScalarBox) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mk *Key) HasQueryGlob() bool {
|
||||||
|
if mk.Key.HasGlob() && len(mk.Edges) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if mk.EdgeIndex != nil && mk.EdgeIndex.Glob && mk.EdgeKey == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if mk.EdgeKey.HasGlob() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type KeyPath struct {
|
type KeyPath struct {
|
||||||
Range Range `json:"range"`
|
Range Range `json:"range"`
|
||||||
Path []*StringBox `json:"path"`
|
Path []*StringBox `json:"path"`
|
||||||
|
|
@ -716,6 +753,37 @@ func (kp *KeyPath) IDA() (ida []string) {
|
||||||
return ida
|
return ida
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (kp *KeyPath) Copy() *KeyPath {
|
||||||
|
kp2 := *kp
|
||||||
|
kp2.Path = nil
|
||||||
|
kp2.Path = append(kp2.Path, kp.Path...)
|
||||||
|
return &kp2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kp *KeyPath) HasDoubleGlob() bool {
|
||||||
|
if kp == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, el := range kp.Path {
|
||||||
|
if el.UnquotedString != nil && el.ScalarString() == "**" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (kp *KeyPath) HasGlob() bool {
|
||||||
|
if kp == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, el := range kp.Path {
|
||||||
|
if el.UnquotedString != nil && len(el.UnquotedString.Pattern) > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
type Edge struct {
|
type Edge struct {
|
||||||
Range Range `json:"range"`
|
Range Range `json:"range"`
|
||||||
|
|
||||||
|
|
@ -1034,6 +1102,10 @@ func (sb *StringBox) Unbox() String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sb *StringBox) ScalarString() string {
|
||||||
|
return sb.Unbox().ScalarString()
|
||||||
|
}
|
||||||
|
|
||||||
// InterpolationBox is used to select between strings and substitutions in unquoted and
|
// InterpolationBox is used to select between strings and substitutions in unquoted and
|
||||||
// double quoted strings. There is no corresponding interface to avoid unnecessary
|
// double quoted strings. There is no corresponding interface to avoid unnecessary
|
||||||
// abstraction.
|
// abstraction.
|
||||||
|
|
@ -1046,12 +1118,11 @@ type InterpolationBox struct {
|
||||||
// & is only special if it begins a key.
|
// & is only special if it begins a key.
|
||||||
// - is only special if followed by another - in a key.
|
// - is only special if followed by another - in a key.
|
||||||
// ' " and | are only special if they begin an unquoted key or value.
|
// ' " and | are only special if they begin an unquoted key or value.
|
||||||
var UnquotedKeySpecials = string([]rune{'#', ';', '\n', '\\', '{', '}', '[', ']', '\'', '"', '|', ':', '.', '-', '<', '>', '*', '&', '(', ')', '@'})
|
var UnquotedKeySpecials = string([]rune{'#', ';', '\n', '\\', '{', '}', '[', ']', '\'', '"', '|', ':', '.', '-', '<', '>', '*', '&', '(', ')', '@', '&'})
|
||||||
var UnquotedValueSpecials = string([]rune{'#', ';', '\n', '\\', '{', '}', '[', ']', '\'', '"', '|', '$', '@'})
|
var UnquotedValueSpecials = string([]rune{'#', ';', '\n', '\\', '{', '}', '[', ']', '\'', '"', '|', '$', '@'})
|
||||||
|
|
||||||
// RawString returns s in a AST String node that can format s in the most aesthetically
|
// RawString returns s in a AST String node that can format s in the most aesthetically
|
||||||
// pleasing way.
|
// pleasing way.
|
||||||
// TODO: Return StringBox
|
|
||||||
func RawString(s string, inKey bool) String {
|
func RawString(s string, inKey bool) String {
|
||||||
if s == "" {
|
if s == "" {
|
||||||
return FlatDoubleQuotedString(s)
|
return FlatDoubleQuotedString(s)
|
||||||
|
|
@ -1064,10 +1135,6 @@ func RawString(s string, inKey bool) String {
|
||||||
if i+1 < len(s) && s[i+1] != '-' {
|
if i+1 < len(s) && s[i+1] != '-' {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
case '&':
|
|
||||||
if i > 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if strings.ContainsRune(UnquotedKeySpecials, r) {
|
if strings.ContainsRune(UnquotedKeySpecials, r) {
|
||||||
if !strings.ContainsRune(s, '"') {
|
if !strings.ContainsRune(s, '"') {
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ func test(t *testing.T, textPath, text string) {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
g, err := d2compiler.Compile("", strings.NewReader(text), nil)
|
g, _, err := d2compiler.Compile("", strings.NewReader(text), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
136
d2cli/main.go
|
|
@ -20,6 +20,7 @@ import (
|
||||||
"oss.terrastruct.com/util-go/go2"
|
"oss.terrastruct.com/util-go/go2"
|
||||||
"oss.terrastruct.com/util-go/xmain"
|
"oss.terrastruct.com/util-go/xmain"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
"oss.terrastruct.com/d2/d2lib"
|
"oss.terrastruct.com/d2/d2lib"
|
||||||
"oss.terrastruct.com/d2/d2parser"
|
"oss.terrastruct.com/d2/d2parser"
|
||||||
"oss.terrastruct.com/d2/d2plugin"
|
"oss.terrastruct.com/d2/d2plugin"
|
||||||
|
|
@ -66,7 +67,8 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
||||||
}
|
}
|
||||||
debugFlag, err := ms.Opts.Bool("DEBUG", "debug", "d", false, "print debug logs.")
|
debugFlag, err := ms.Opts.Bool("DEBUG", "debug", "d", false, "print debug logs.")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
ms.Log.Warn.Printf("Invalid DEBUG flag value ignored")
|
||||||
|
debugFlag = go2.Pointer(false)
|
||||||
}
|
}
|
||||||
imgCacheFlag, err := ms.Opts.Bool("IMG_CACHE", "img-cache", "", true, "in watch mode, images used in icons are cached for subsequent compilations. This should be disabled if images might change.")
|
imgCacheFlag, err := ms.Opts.Bool("IMG_CACHE", "img-cache", "", true, "in watch mode, images used in icons are cached for subsequent compilations. This should be disabled if images might change.")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -117,11 +119,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.")
|
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.")
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = populateLayoutOpts(ctx, ms, ps)
|
err = populateLayoutOpts(ctx, ms, plugins)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -146,7 +148,7 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
||||||
case "init-playwright":
|
case "init-playwright":
|
||||||
return initPlaywright()
|
return initPlaywright()
|
||||||
case "layout":
|
case "layout":
|
||||||
return layoutCmd(ctx, ms, ps)
|
return layoutCmd(ctx, ms, plugins)
|
||||||
case "themes":
|
case "themes":
|
||||||
themesCmd(ctx, ms)
|
themesCmd(ctx, ms)
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -226,6 +228,38 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
||||||
}
|
}
|
||||||
ms.Log.Debug.Printf("using theme %s (ID: %d)", match.Name, *themeFlag)
|
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 {
|
if *darkThemeFlag == -1 {
|
||||||
darkThemeFlag = nil // TODO this is a temporary solution: https://github.com/terrastruct/util-go/issues/7
|
darkThemeFlag = nil // TODO this is a temporary solution: https://github.com/terrastruct/util-go/issues/7
|
||||||
}
|
}
|
||||||
|
|
@ -241,29 +275,6 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
||||||
scale = scaleFlag
|
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 !outputFormat.supportsDarkTheme() {
|
||||||
if darkThemeFlag != nil {
|
if darkThemeFlag != nil {
|
||||||
ms.Log.Warn.Printf("--dark-theme cannot be used while exporting to another format other than .svg")
|
ms.Log.Warn.Printf("--dark-theme cannot be used while exporting to another format other than .svg")
|
||||||
|
|
@ -285,10 +296,10 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
renderOpts := d2svg.RenderOpts{
|
renderOpts := d2svg.RenderOpts{
|
||||||
Pad: int(*padFlag),
|
Pad: padFlag,
|
||||||
Sketch: *sketchFlag,
|
Sketch: sketchFlag,
|
||||||
Center: *centerFlag,
|
Center: centerFlag,
|
||||||
ThemeID: *themeFlag,
|
ThemeID: themeFlag,
|
||||||
DarkThemeID: darkThemeFlag,
|
DarkThemeID: darkThemeFlag,
|
||||||
Scale: scale,
|
Scale: scale,
|
||||||
}
|
}
|
||||||
|
|
@ -298,7 +309,8 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
||||||
return xmain.UsageErrorf("-w[atch] cannot be combined with reading input from stdin")
|
return xmain.UsageErrorf("-w[atch] cannot be combined with reading input from stdin")
|
||||||
}
|
}
|
||||||
w, err := newWatcher(ctx, ms, watcherOpts{
|
w, err := newWatcher(ctx, ms, watcherOpts{
|
||||||
layoutPlugin: plugin,
|
plugins: plugins,
|
||||||
|
layout: layoutFlag,
|
||||||
renderOpts: renderOpts,
|
renderOpts: renderOpts,
|
||||||
animateInterval: *animateIntervalFlag,
|
animateInterval: *animateIntervalFlag,
|
||||||
host: *hostFlag,
|
host: *hostFlag,
|
||||||
|
|
@ -319,7 +331,7 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
||||||
ctx, cancel := timelib.WithTimeout(ctx, time.Minute*2)
|
ctx, cancel := timelib.WithTimeout(ctx, time.Minute*2)
|
||||||
defer cancel()
|
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 err != nil {
|
||||||
if written {
|
if written {
|
||||||
return fmt.Errorf("failed to fully compile (partial render written): %w", err)
|
return fmt.Errorf("failed to fully compile (partial render written): %w", err)
|
||||||
|
|
@ -329,7 +341,32 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
|
||||||
return nil
|
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, boardPath string, bundle, forceAppendix bool, page playwright.Page) (_ []byte, written bool, _ error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
input, err := ms.ReadPath(inputPath)
|
input, err := ms.ReadPath(inputPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -341,16 +378,12 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, rende
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
layout := plugin.Layout
|
|
||||||
opts := &d2lib.CompileOptions{
|
opts := &d2lib.CompileOptions{
|
||||||
Layout: layout,
|
|
||||||
Ruler: ruler,
|
Ruler: ruler,
|
||||||
ThemeID: renderOpts.ThemeID,
|
|
||||||
FontFamily: fontFamily,
|
FontFamily: fontFamily,
|
||||||
InputPath: inputPath,
|
InputPath: inputPath,
|
||||||
}
|
LayoutResolver: LayoutResolver(ctx, ms, plugins),
|
||||||
if renderOpts.Sketch {
|
Layout: layout,
|
||||||
opts.FontFamily = go2.Pointer(d2fonts.HandDrawn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cancel := background.Repeat(func() {
|
cancel := background.Repeat(func() {
|
||||||
|
|
@ -358,12 +391,14 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, rende
|
||||||
}, time.Second*5)
|
}, time.Second*5)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
diagram, g, err := d2lib.Compile(ctx, string(input), opts)
|
diagram, g, err := d2lib.Compile(ctx, string(input), opts, &renderOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
cancel()
|
cancel()
|
||||||
|
|
||||||
|
plugin, _ := d2plugin.FindPlugin(ctx, plugins, *opts.Layout)
|
||||||
|
|
||||||
if animateInterval > 0 {
|
if animateInterval > 0 {
|
||||||
masterID, err := diagram.HashID()
|
masterID, err := diagram.HashID()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -372,6 +407,16 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, rende
|
||||||
renderOpts.MasterID = masterID
|
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)
|
pluginInfo, err := plugin.Info(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
|
|
@ -455,7 +500,12 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, rende
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boards, err := render(ctx, ms, compileDur, plugin, renderOpts, inputPath, outputPath, bundle, forceAppendix, page, ruler, diagram)
|
board := diagram.GetBoard(boardPath)
|
||||||
|
if board == nil {
|
||||||
|
return nil, false, fmt.Errorf("Diagram with path %s not found", boardPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
boards, err := render(ctx, ms, compileDur, plugin, renderOpts, inputPath, outputPath, bundle, forceAppendix, page, ruler, board)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, false, err
|
return nil, false, err
|
||||||
}
|
}
|
||||||
|
|
@ -805,7 +855,7 @@ func renderPDF(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, opt
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return svg, err
|
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 {
|
if err != nil {
|
||||||
return svg, err
|
return svg, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,7 @@ function init(reconnectDelay) {
|
||||||
const d2SVG = window.document.querySelector("#d2-svg-container");
|
const d2SVG = window.document.querySelector("#d2-svg-container");
|
||||||
|
|
||||||
const devMode = document.body.dataset.d2DevMode === "true";
|
const devMode = document.body.dataset.d2DevMode === "true";
|
||||||
const ws = new WebSocket(
|
const ws = new WebSocket(`ws://${window.location.host}/watch`);
|
||||||
`ws://${window.location.host}${window.location.pathname}watch`
|
|
||||||
);
|
|
||||||
let isInit = true;
|
let isInit = true;
|
||||||
let ratio;
|
let ratio;
|
||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
|
|
@ -28,7 +26,7 @@ function init(reconnectDelay) {
|
||||||
// we can't just set `d2SVG.innerHTML = msg.svg` need to parse this as xml not html
|
// we can't just set `d2SVG.innerHTML = msg.svg` need to parse this as xml not html
|
||||||
const parsedXML = new DOMParser().parseFromString(msg.svg, "text/xml");
|
const parsedXML = new DOMParser().parseFromString(msg.svg, "text/xml");
|
||||||
d2SVG.replaceChildren(parsedXML.documentElement);
|
d2SVG.replaceChildren(parsedXML.documentElement);
|
||||||
changeFavicon("./static/favicon.ico");
|
changeFavicon("/static/favicon.ico");
|
||||||
const svgEl = d2SVG.querySelector("#d2-svg");
|
const svgEl = d2SVG.querySelector("#d2-svg");
|
||||||
// just use inner SVG in watch mode
|
// just use inner SVG in watch mode
|
||||||
svgEl.parentElement.replaceWith(svgEl);
|
svgEl.parentElement.replaceWith(svgEl);
|
||||||
|
|
@ -60,7 +58,7 @@ function init(reconnectDelay) {
|
||||||
if (msg.err) {
|
if (msg.err) {
|
||||||
d2ErrDiv.innerText = msg.err;
|
d2ErrDiv.innerText = msg.err;
|
||||||
d2ErrDiv.style.display = "block";
|
d2ErrDiv.style.display = "block";
|
||||||
changeFavicon("./static/favicon-err.ico");
|
changeFavicon("/static/favicon-err.ico");
|
||||||
d2ErrDiv.scrollIntoView();
|
d2ErrDiv.scrollIntoView();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -41,13 +42,15 @@ var devMode = false
|
||||||
var staticFS embed.FS
|
var staticFS embed.FS
|
||||||
|
|
||||||
type watcherOpts struct {
|
type watcherOpts struct {
|
||||||
layoutPlugin d2plugin.Plugin
|
layout *string
|
||||||
|
plugins []d2plugin.Plugin
|
||||||
renderOpts d2svg.RenderOpts
|
renderOpts d2svg.RenderOpts
|
||||||
animateInterval int64
|
animateInterval int64
|
||||||
host string
|
host string
|
||||||
port string
|
port string
|
||||||
inputPath string
|
inputPath string
|
||||||
outputPath string
|
outputPath string
|
||||||
|
boardPath string
|
||||||
pwd string
|
pwd string
|
||||||
bundle bool
|
bundle bool
|
||||||
forceAppendix bool
|
forceAppendix bool
|
||||||
|
|
@ -361,7 +364,7 @@ func (w *watcher) compileLoop(ctx context.Context) error {
|
||||||
w.pw = newPW
|
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.boardPath, w.bundle, w.forceAppendix, w.pw.Page)
|
||||||
errs := ""
|
errs := ""
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if len(svg) > 0 {
|
if len(svg) > 0 {
|
||||||
|
|
@ -429,15 +432,25 @@ func (w *watcher) handleRoot(hw http.ResponseWriter, r *http.Request) {
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>%s</title>
|
<title>%s</title>
|
||||||
<script src="./static/watch.js"></script>
|
<script src="/static/watch.js"></script>
|
||||||
<link rel="stylesheet" href="./static/watch.css">
|
<link rel="stylesheet" href="/static/watch.css">
|
||||||
<link id="favicon" rel="icon" href="./static/favicon.ico">
|
<link id="favicon" rel="icon" href="/static/favicon.ico">
|
||||||
</head>
|
</head>
|
||||||
<body data-d2-dev-mode=%t>
|
<body data-d2-dev-mode=%t>
|
||||||
<div id="d2-err" style="display: none"></div>
|
<div id="d2-err" style="display: none"></div>
|
||||||
<div id="d2-svg-container"></div>
|
<div id="d2-svg-container"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>`, filepath.Base(w.outputPath), w.devMode)
|
</html>`, filepath.Base(w.outputPath), w.devMode)
|
||||||
|
|
||||||
|
// if path is "/x.svg", we just want "x"
|
||||||
|
boardPath := strings.TrimPrefix(r.URL.Path, "/")
|
||||||
|
if idx := strings.LastIndexByte(boardPath, '.'); idx != -1 {
|
||||||
|
boardPath = boardPath[:idx]
|
||||||
|
}
|
||||||
|
if boardPath != w.boardPath {
|
||||||
|
w.boardPath = boardPath
|
||||||
|
w.requestCompile()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *watcher) handleWatch(hw http.ResponseWriter, r *http.Request) error {
|
func (w *watcher) handleWatch(hw http.ResponseWriter, r *http.Request) error {
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ type CompileOptions struct {
|
||||||
FS fs.FS
|
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 {
|
if opts == nil {
|
||||||
opts = &CompileOptions{}
|
opts = &CompileOptions{}
|
||||||
}
|
}
|
||||||
|
|
@ -36,7 +36,7 @@ func Compile(p string, r io.RuneReader, opts *CompileOptions) (*d2graph.Graph, e
|
||||||
UTF16: opts.UTF16,
|
UTF16: opts.UTF16,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
ir, err := d2ir.Compile(ast, &d2ir.CompileOptions{
|
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,
|
FS: opts.FS,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
g, err := compileIR(ast, ir)
|
g, err := compileIR(ast, ir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
g.SortObjectsByAST()
|
g.SortObjectsByAST()
|
||||||
g.SortEdgesByAST()
|
g.SortEdgesByAST()
|
||||||
return g, nil
|
return g, compileConfig(ir), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func compileIR(ast *d2ast.Map, m *d2ir.Map) (*d2graph.Graph, error) {
|
func compileIR(ast *d2ast.Map, m *d2ir.Map) (*d2graph.Graph, error) {
|
||||||
|
|
@ -92,6 +92,9 @@ func (c *compiler) compileBoard(g *d2graph.Graph, ir *d2ir.Map) *d2graph.Graph {
|
||||||
g.IsFolderOnly = true
|
g.IsFolderOnly = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(g.Objects) == 0 {
|
||||||
|
g.IsFolderOnly = true
|
||||||
|
}
|
||||||
return g
|
return g
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -277,6 +280,8 @@ func (c *compiler) compileField(obj *d2graph.Object, f *d2ir.Field) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
} else if f.Name == "vars" {
|
||||||
|
return
|
||||||
} else if isReserved {
|
} else if isReserved {
|
||||||
c.compileReserved(&obj.Attributes, f)
|
c.compileReserved(&obj.Attributes, f)
|
||||||
return
|
return
|
||||||
|
|
@ -329,7 +334,7 @@ func (c *compiler) compileField(obj *d2graph.Object, f *d2ir.Field) {
|
||||||
Scope: fr.Context.Scope,
|
Scope: fr.Context.Scope,
|
||||||
ScopeAST: fr.Context.ScopeAST,
|
ScopeAST: fr.Context.ScopeAST,
|
||||||
}
|
}
|
||||||
if fr.Context.ScopeMap != nil {
|
if fr.Context.ScopeMap != nil && !d2ir.IsVar(fr.Context.ScopeMap) {
|
||||||
scopeObjIDA := d2graphIDA(d2ir.BoardIDA(fr.Context.ScopeMap))
|
scopeObjIDA := d2graphIDA(d2ir.BoardIDA(fr.Context.ScopeMap))
|
||||||
r.ScopeObj = obj.Graph.Root.EnsureChild(scopeObjIDA)
|
r.ScopeObj = obj.Graph.Root.EnsureChild(scopeObjIDA)
|
||||||
}
|
}
|
||||||
|
|
@ -725,7 +730,7 @@ func (c *compiler) compileEdge(obj *d2graph.Object, e *d2ir.Edge) {
|
||||||
Scope: er.Context.Scope,
|
Scope: er.Context.Scope,
|
||||||
ScopeAST: er.Context.ScopeAST,
|
ScopeAST: er.Context.ScopeAST,
|
||||||
}
|
}
|
||||||
if er.Context.ScopeMap != nil {
|
if er.Context.ScopeMap != nil && !d2ir.IsVar(er.Context.ScopeMap) {
|
||||||
scopeObjIDA := d2graphIDA(d2ir.BoardIDA(er.Context.ScopeMap))
|
scopeObjIDA := d2graphIDA(d2ir.BoardIDA(er.Context.ScopeMap))
|
||||||
r.ScopeObj = edge.Src.Graph.Root.EnsureChild(scopeObjIDA)
|
r.ScopeObj = edge.Src.Graph.Root.EnsureChild(scopeObjIDA)
|
||||||
}
|
}
|
||||||
|
|
@ -1283,3 +1288,45 @@ func parentSeqDiagram(n d2ir.Node) *d2ir.Map {
|
||||||
n = m
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,15 @@ import (
|
||||||
|
|
||||||
"oss.terrastruct.com/util-go/assert"
|
"oss.terrastruct.com/util-go/assert"
|
||||||
"oss.terrastruct.com/util-go/diff"
|
"oss.terrastruct.com/util-go/diff"
|
||||||
|
"oss.terrastruct.com/util-go/go2"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2compiler"
|
"oss.terrastruct.com/d2/d2compiler"
|
||||||
"oss.terrastruct.com/d2/d2exporter"
|
"oss.terrastruct.com/d2/d2exporter"
|
||||||
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2grid"
|
"oss.terrastruct.com/d2/d2layouts/d2grid"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2sequence"
|
"oss.terrastruct.com/d2/d2layouts/d2sequence"
|
||||||
|
"oss.terrastruct.com/d2/d2lib"
|
||||||
"oss.terrastruct.com/d2/d2target"
|
"oss.terrastruct.com/d2/d2target"
|
||||||
"oss.terrastruct.com/d2/lib/geo"
|
"oss.terrastruct.com/d2/lib/geo"
|
||||||
"oss.terrastruct.com/d2/lib/log"
|
"oss.terrastruct.com/d2/lib/log"
|
||||||
|
|
@ -219,7 +222,7 @@ func run(t *testing.T, tc testCase) {
|
||||||
ctx = log.WithTB(ctx, t, nil)
|
ctx = log.WithTB(ctx, t, nil)
|
||||||
ctx = log.Leveled(ctx, slog.LevelDebug)
|
ctx = log.Leveled(ctx, slog.LevelDebug)
|
||||||
|
|
||||||
g, err := d2compiler.Compile("", strings.NewReader(tc.dsl), &d2compiler.CompileOptions{
|
g, config, err := d2compiler.Compile("", strings.NewReader(tc.dsl), &d2compiler.CompileOptions{
|
||||||
UTF16: true,
|
UTF16: true,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -241,6 +244,9 @@ func run(t *testing.T, tc testCase) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
if got != nil {
|
||||||
|
got.Config = config
|
||||||
|
}
|
||||||
|
|
||||||
if tc.assertions != nil {
|
if tc.assertions != nil {
|
||||||
t.Run("assertions", func(t *testing.T) {
|
t.Run("assertions", func(t *testing.T) {
|
||||||
|
|
@ -267,3 +273,57 @@ func run(t *testing.T, tc testCase) {
|
||||||
err = diff.TestdataJSON(filepath.Join("..", "testdata", "d2exporter", t.Name()), got)
|
err = diff.TestdataJSON(filepath.Join("..", "testdata", "d2exporter", t.Name()), got)
|
||||||
assert.Success(t, err)
|
assert.Success(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestHashID tests that 2 diagrams with different theme configs do not equal each other
|
||||||
|
func TestHashID(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
ctx = log.WithTB(ctx, t, nil)
|
||||||
|
ctx = log.Leveled(ctx, slog.LevelDebug)
|
||||||
|
|
||||||
|
aString := `
|
||||||
|
vars: {
|
||||||
|
d2-config: {
|
||||||
|
theme-id: 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a -> b
|
||||||
|
`
|
||||||
|
|
||||||
|
bString := `
|
||||||
|
vars: {
|
||||||
|
d2-config: {
|
||||||
|
theme-id: 4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a -> b
|
||||||
|
`
|
||||||
|
|
||||||
|
da, err := compile(ctx, aString)
|
||||||
|
assert.JSON(t, nil, err)
|
||||||
|
|
||||||
|
db, err := compile(ctx, bString)
|
||||||
|
assert.JSON(t, nil, err)
|
||||||
|
|
||||||
|
hashA, err := da.HashID()
|
||||||
|
assert.JSON(t, nil, err)
|
||||||
|
|
||||||
|
hashB, err := db.HashID()
|
||||||
|
assert.JSON(t, nil, err)
|
||||||
|
|
||||||
|
assert.NotEqual(t, hashA, hashB)
|
||||||
|
}
|
||||||
|
|
||||||
|
func layoutResolver(engine string) (d2graph.LayoutGraph, error) {
|
||||||
|
return d2dagrelayout.DefaultLayout, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func compile(ctx context.Context, d2 string) (*d2target.Diagram, error) {
|
||||||
|
ruler, _ := textmeasure.NewRuler()
|
||||||
|
opts := &d2lib.CompileOptions{
|
||||||
|
Ruler: ruler,
|
||||||
|
LayoutResolver: layoutResolver,
|
||||||
|
Layout: go2.Pointer("dagre"),
|
||||||
|
}
|
||||||
|
d, _, e := d2lib.Compile(ctx, d2, opts, nil)
|
||||||
|
return d, e
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1651,6 +1651,7 @@ var SimpleReservedKeywords = map[string]struct{}{
|
||||||
"vertical-gap": {},
|
"vertical-gap": {},
|
||||||
"horizontal-gap": {},
|
"horizontal-gap": {},
|
||||||
"class": {},
|
"class": {},
|
||||||
|
"vars": {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReservedKeywordHolders are reserved keywords that are meaningless on its own and must hold composites
|
// ReservedKeywordHolders are reserved keywords that are meaningless on its own and must hold composites
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package d2graph
|
package d2graph
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2target"
|
"oss.terrastruct.com/d2/d2target"
|
||||||
|
|
@ -316,10 +317,29 @@ func (obj *Object) GetLabelTopLeft() *geo.Point {
|
||||||
return labelTL
|
return labelTL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (obj *Object) GetIconTopLeft() *geo.Point {
|
||||||
|
if obj.IconPosition == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
s := obj.ToShape()
|
||||||
|
iconPosition := label.Position(*obj.IconPosition)
|
||||||
|
|
||||||
|
var box *geo.Box
|
||||||
|
if iconPosition.IsOutside() {
|
||||||
|
box = s.GetBox()
|
||||||
|
} else {
|
||||||
|
box = s.GetInnerBox()
|
||||||
|
}
|
||||||
|
|
||||||
|
return iconPosition.GetPointOnBox(box, label.PADDING, d2target.MAX_ICON_SIZE, d2target.MAX_ICON_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
func (edge *Edge) TraceToShape(points []*geo.Point, startIndex, endIndex int) (newStart, newEnd int) {
|
func (edge *Edge) TraceToShape(points []*geo.Point, startIndex, endIndex int) (newStart, newEnd int) {
|
||||||
srcShape := edge.Src.ToShape()
|
srcShape := edge.Src.ToShape()
|
||||||
dstShape := edge.Dst.ToShape()
|
dstShape := edge.Dst.ToShape()
|
||||||
|
|
||||||
|
startingSegment := geo.Segment{Start: points[startIndex+1], End: points[startIndex]}
|
||||||
// if an edge runs into an outside label, stop the edge at the label instead
|
// if an edge runs into an outside label, stop the edge at the label instead
|
||||||
overlapsOutsideLabel := false
|
overlapsOutsideLabel := false
|
||||||
if edge.Src.HasLabel() {
|
if edge.Src.HasLabel() {
|
||||||
|
|
@ -330,18 +350,27 @@ func (edge *Edge) TraceToShape(points []*geo.Point, startIndex, endIndex int) (n
|
||||||
labelHeight := float64(edge.Src.LabelDimensions.Height)
|
labelHeight := float64(edge.Src.LabelDimensions.Height)
|
||||||
labelTL := labelPosition.GetPointOnBox(edge.Src.Box, label.PADDING, labelWidth, labelHeight)
|
labelTL := labelPosition.GetPointOnBox(edge.Src.Box, label.PADDING, labelWidth, labelHeight)
|
||||||
|
|
||||||
startingSegment := geo.Segment{Start: points[startIndex+1], End: points[startIndex]}
|
|
||||||
labelBox := geo.NewBox(labelTL, labelWidth, labelHeight)
|
labelBox := geo.NewBox(labelTL, labelWidth, labelHeight)
|
||||||
// add left/right padding to box
|
// add left/right padding to box
|
||||||
labelBox.TopLeft.X -= label.PADDING
|
labelBox.TopLeft.X -= label.PADDING
|
||||||
labelBox.Width += 2 * label.PADDING
|
labelBox.Width += 2 * label.PADDING
|
||||||
|
|
||||||
|
for labelBox.Contains(startingSegment.End) && startIndex+1 > endIndex {
|
||||||
|
startingSegment.Start = startingSegment.End
|
||||||
|
startingSegment.End = points[startIndex+2]
|
||||||
|
startIndex++
|
||||||
|
}
|
||||||
if intersections := labelBox.Intersections(startingSegment); len(intersections) > 0 {
|
if intersections := labelBox.Intersections(startingSegment); len(intersections) > 0 {
|
||||||
overlapsOutsideLabel = true
|
overlapsOutsideLabel = true
|
||||||
|
p := intersections[0]
|
||||||
|
if len(intersections) > 1 {
|
||||||
|
p = findOuterIntersection(labelPosition, intersections)
|
||||||
|
}
|
||||||
// move starting segment to label intersection point
|
// move starting segment to label intersection point
|
||||||
points[startIndex] = intersections[0]
|
points[startIndex] = p
|
||||||
startingSegment.End = intersections[0]
|
startingSegment.End = p
|
||||||
// if the segment becomes too short, just merge it with the next segment
|
// if the segment becomes too short, just merge it with the next segment
|
||||||
if startIndex < len(points) && startingSegment.Length() < MIN_SEGMENT_LEN {
|
if startIndex+1 < endIndex && startingSegment.Length() < MIN_SEGMENT_LEN {
|
||||||
points[startIndex+1] = points[startIndex]
|
points[startIndex+1] = points[startIndex]
|
||||||
startIndex++
|
startIndex++
|
||||||
}
|
}
|
||||||
|
|
@ -349,9 +378,20 @@ func (edge *Edge) TraceToShape(points []*geo.Point, startIndex, endIndex int) (n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !overlapsOutsideLabel {
|
if !overlapsOutsideLabel {
|
||||||
|
if intersections := edge.Src.Intersections(startingSegment); len(intersections) > 0 {
|
||||||
|
// move starting segment to intersection point
|
||||||
|
points[startIndex] = intersections[0]
|
||||||
|
startingSegment.End = intersections[0]
|
||||||
|
// if the segment becomes too short, just merge it with the next segment
|
||||||
|
if startIndex+1 < endIndex && startingSegment.Length() < MIN_SEGMENT_LEN {
|
||||||
|
points[startIndex+1] = points[startIndex]
|
||||||
|
startIndex++
|
||||||
|
}
|
||||||
|
}
|
||||||
// trace the edge to the specific shape's border
|
// trace the edge to the specific shape's border
|
||||||
points[startIndex] = shape.TraceToShapeBorder(srcShape, points[startIndex], points[startIndex+1])
|
points[startIndex] = shape.TraceToShapeBorder(srcShape, points[startIndex], points[startIndex+1])
|
||||||
}
|
}
|
||||||
|
endingSegment := geo.Segment{Start: points[endIndex-1], End: points[endIndex]}
|
||||||
overlapsOutsideLabel = false
|
overlapsOutsideLabel = false
|
||||||
if edge.Dst.HasLabel() {
|
if edge.Dst.HasLabel() {
|
||||||
// assumes LabelPosition, LabelWidth, LabelHeight are all set if there is a label
|
// assumes LabelPosition, LabelWidth, LabelHeight are all set if there is a label
|
||||||
|
|
@ -361,18 +401,26 @@ func (edge *Edge) TraceToShape(points []*geo.Point, startIndex, endIndex int) (n
|
||||||
labelHeight := float64(edge.Dst.LabelDimensions.Height)
|
labelHeight := float64(edge.Dst.LabelDimensions.Height)
|
||||||
labelTL := labelPosition.GetPointOnBox(edge.Dst.Box, label.PADDING, labelWidth, labelHeight)
|
labelTL := labelPosition.GetPointOnBox(edge.Dst.Box, label.PADDING, labelWidth, labelHeight)
|
||||||
|
|
||||||
endingSegment := geo.Segment{Start: points[endIndex-1], End: points[endIndex]}
|
|
||||||
labelBox := geo.NewBox(labelTL, labelWidth, labelHeight)
|
labelBox := geo.NewBox(labelTL, labelWidth, labelHeight)
|
||||||
// add left/right padding to box
|
// add left/right padding to box
|
||||||
labelBox.TopLeft.X -= label.PADDING
|
labelBox.TopLeft.X -= label.PADDING
|
||||||
labelBox.Width += 2 * label.PADDING
|
labelBox.Width += 2 * label.PADDING
|
||||||
|
for labelBox.Contains(endingSegment.Start) && endIndex-1 > startIndex {
|
||||||
|
endingSegment.End = endingSegment.Start
|
||||||
|
endingSegment.Start = points[endIndex-2]
|
||||||
|
endIndex--
|
||||||
|
}
|
||||||
if intersections := labelBox.Intersections(endingSegment); len(intersections) > 0 {
|
if intersections := labelBox.Intersections(endingSegment); len(intersections) > 0 {
|
||||||
overlapsOutsideLabel = true
|
overlapsOutsideLabel = true
|
||||||
|
p := intersections[0]
|
||||||
|
if len(intersections) > 1 {
|
||||||
|
p = findOuterIntersection(labelPosition, intersections)
|
||||||
|
}
|
||||||
// move ending segment to label intersection point
|
// move ending segment to label intersection point
|
||||||
points[endIndex] = intersections[0]
|
points[endIndex] = p
|
||||||
endingSegment.End = intersections[0]
|
endingSegment.End = p
|
||||||
// if the segment becomes too short, just merge it with the previous segment
|
// if the segment becomes too short, just merge it with the previous segment
|
||||||
if endIndex-1 > 0 && endingSegment.Length() < MIN_SEGMENT_LEN {
|
if endIndex-1 > startIndex && endingSegment.Length() < MIN_SEGMENT_LEN {
|
||||||
points[endIndex-1] = points[endIndex]
|
points[endIndex-1] = points[endIndex]
|
||||||
endIndex--
|
endIndex--
|
||||||
}
|
}
|
||||||
|
|
@ -380,7 +428,39 @@ func (edge *Edge) TraceToShape(points []*geo.Point, startIndex, endIndex int) (n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !overlapsOutsideLabel {
|
if !overlapsOutsideLabel {
|
||||||
|
if intersections := edge.Dst.Intersections(endingSegment); len(intersections) > 0 {
|
||||||
|
// move ending segment to intersection point
|
||||||
|
points[endIndex] = intersections[0]
|
||||||
|
endingSegment.End = intersections[0]
|
||||||
|
// if the segment becomes too short, just merge it with the previous segment
|
||||||
|
if endIndex-1 > startIndex && endingSegment.Length() < MIN_SEGMENT_LEN {
|
||||||
|
points[endIndex-1] = points[endIndex]
|
||||||
|
endIndex--
|
||||||
|
}
|
||||||
|
}
|
||||||
points[endIndex] = shape.TraceToShapeBorder(dstShape, points[endIndex], points[endIndex-1])
|
points[endIndex] = shape.TraceToShapeBorder(dstShape, points[endIndex], points[endIndex-1])
|
||||||
}
|
}
|
||||||
return startIndex, endIndex
|
return startIndex, endIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findOuterIntersection(labelPosition label.Position, intersections []*geo.Point) *geo.Point {
|
||||||
|
switch labelPosition {
|
||||||
|
case label.OutsideTopLeft, label.OutsideTopRight, label.OutsideTopCenter:
|
||||||
|
sort.Slice(intersections, func(i, j int) bool {
|
||||||
|
return intersections[i].Y < intersections[j].Y
|
||||||
|
})
|
||||||
|
case label.OutsideBottomLeft, label.OutsideBottomRight, label.OutsideBottomCenter:
|
||||||
|
sort.Slice(intersections, func(i, j int) bool {
|
||||||
|
return intersections[i].Y > intersections[j].Y
|
||||||
|
})
|
||||||
|
case label.OutsideLeftTop, label.OutsideLeftMiddle, label.OutsideLeftBottom:
|
||||||
|
sort.Slice(intersections, func(i, j int) bool {
|
||||||
|
return intersections[i].X < intersections[j].X
|
||||||
|
})
|
||||||
|
case label.OutsideRightTop, label.OutsideRightMiddle, label.OutsideRightBottom:
|
||||||
|
sort.Slice(intersections, func(i, j int) bool {
|
||||||
|
return intersections[i].X > intersections[j].X
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return intersections[0]
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import (
|
||||||
func TestSerialization(t *testing.T) {
|
func TestSerialization(t *testing.T) {
|
||||||
t.Parallel()
|
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)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
asserts := func(g *d2graph.Graph) {
|
asserts := func(g *d2graph.Graph) {
|
||||||
|
|
@ -53,7 +53,7 @@ func TestCasingRegression(t *testing.T) {
|
||||||
|
|
||||||
script := `UserCreatedTypeField`
|
script := `UserCreatedTypeField`
|
||||||
|
|
||||||
g, err := d2compiler.Compile("", strings.NewReader(script), nil)
|
g, _, err := d2compiler.Compile("", strings.NewReader(script), nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
_, ok := g.Root.HasChild([]string{"UserCreatedTypeField"})
|
_, ok := g.Root.HasChild([]string{"UserCreatedTypeField"})
|
||||||
|
|
|
||||||
419
d2ir/compile.go
|
|
@ -2,11 +2,15 @@ package d2ir
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2ast"
|
"oss.terrastruct.com/d2/d2ast"
|
||||||
"oss.terrastruct.com/d2/d2format"
|
"oss.terrastruct.com/d2/d2format"
|
||||||
"oss.terrastruct.com/d2/d2parser"
|
"oss.terrastruct.com/d2/d2parser"
|
||||||
|
"oss.terrastruct.com/d2/d2themes"
|
||||||
|
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
|
||||||
|
"oss.terrastruct.com/util-go/go2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type compiler struct {
|
type compiler struct {
|
||||||
|
|
@ -18,6 +22,8 @@ type compiler struct {
|
||||||
// importCache enables reuse of files imported multiple times.
|
// importCache enables reuse of files imported multiple times.
|
||||||
importCache map[string]*Map
|
importCache map[string]*Map
|
||||||
utf16 bool
|
utf16 bool
|
||||||
|
|
||||||
|
globStack []bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type CompileOptions struct {
|
type CompileOptions struct {
|
||||||
|
|
@ -52,14 +58,15 @@ func Compile(ast *d2ast.Map, opts *CompileOptions) (*Map, error) {
|
||||||
defer c.popImportStack()
|
defer c.popImportStack()
|
||||||
|
|
||||||
c.compileMap(m, ast, ast)
|
c.compileMap(m, ast, ast)
|
||||||
c.compileClasses(m)
|
c.compileSubstitutions(m, nil)
|
||||||
|
c.overlayClasses(m)
|
||||||
if !c.err.Empty() {
|
if !c.err.Empty() {
|
||||||
return nil, c.err
|
return nil, c.err
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compiler) compileClasses(m *Map) {
|
func (c *compiler) overlayClasses(m *Map) {
|
||||||
classes := m.GetField("classes")
|
classes := m.GetField("classes")
|
||||||
if classes == nil || classes.Map() == nil {
|
if classes == nil || classes.Map() == nil {
|
||||||
return
|
return
|
||||||
|
|
@ -92,10 +99,242 @@ func (c *compiler) compileClasses(m *Map) {
|
||||||
l.Fields = append(l.Fields, base)
|
l.Fields = append(l.Fields, base)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.compileClasses(l)
|
c.overlayClasses(l)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *compiler) compileSubstitutions(m *Map, varsStack []*Map) {
|
||||||
|
for _, f := range m.Fields {
|
||||||
|
if f.Name == "vars" && f.Map() != nil {
|
||||||
|
varsStack = append([]*Map{f.Map()}, varsStack...)
|
||||||
|
}
|
||||||
|
if f.Primary() != nil {
|
||||||
|
c.resolveSubstitutions(varsStack, f)
|
||||||
|
}
|
||||||
|
if arr, ok := f.Composite.(*Array); ok {
|
||||||
|
for _, val := range arr.Values {
|
||||||
|
if scalar, ok := val.(*Scalar); ok {
|
||||||
|
c.resolveSubstitutions(varsStack, scalar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if f.Map() != nil {
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, e := range m.Edges {
|
||||||
|
if e.Primary() != nil {
|
||||||
|
c.resolveSubstitutions(varsStack, e)
|
||||||
|
}
|
||||||
|
if e.Map() != nil {
|
||||||
|
c.compileSubstitutions(e.Map(), varsStack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
switch s := node.Primary().Value.(type) {
|
||||||
|
case *d2ast.UnquotedString:
|
||||||
|
for i, box := range s.Value {
|
||||||
|
if box.Substitution != nil {
|
||||||
|
for _, vars := range varsStack {
|
||||||
|
resolvedField = c.resolveSubstitution(vars, box.Substitution)
|
||||||
|
if resolvedField != nil {
|
||||||
|
if resolvedField.Primary() != nil {
|
||||||
|
if _, ok := resolvedField.Primary().Value.(*d2ast.Null); ok {
|
||||||
|
resolvedField = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if resolvedField == nil {
|
||||||
|
c.errorf(node.LastRef().AST(), `could not resolve variable "%s"`, strings.Join(box.Substitution.IDA(), "."))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if box.Substitution.Spread {
|
||||||
|
if resolvedField.Composite == nil {
|
||||||
|
c.errorf(box.Substitution, "cannot spread non-composite")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch n := node.(type) {
|
||||||
|
case *Scalar: // Array value
|
||||||
|
resolvedArr, ok := resolvedField.Composite.(*Array)
|
||||||
|
if !ok {
|
||||||
|
c.errorf(box.Substitution, "cannot spread non-array into array")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
arr := n.parent.(*Array)
|
||||||
|
for i, s := range arr.Values {
|
||||||
|
if s == n {
|
||||||
|
arr.Values = append(append(arr.Values[:i], resolvedArr.Values...), arr.Values[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *Field:
|
||||||
|
if resolvedField.Map() != nil {
|
||||||
|
OverlayMap(ParentMap(n), resolvedField.Map())
|
||||||
|
}
|
||||||
|
// Remove the placeholder field
|
||||||
|
m := n.parent.(*Map)
|
||||||
|
for i, f2 := range m.Fields {
|
||||||
|
if n == f2 {
|
||||||
|
m.Fields = append(m.Fields[:i], m.Fields[i+1:]...)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if resolvedField.Primary() == nil {
|
||||||
|
if resolvedField.Composite == nil {
|
||||||
|
c.errorf(node.LastRef().AST(), `cannot substitute variable without value: "%s"`, strings.Join(box.Substitution.IDA(), "."))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(s.Value) > 1 {
|
||||||
|
c.errorf(node.LastRef().AST(), `cannot substitute composite variable "%s" as part of a string`, strings.Join(box.Substitution.IDA(), "."))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch n := node.(type) {
|
||||||
|
case *Field:
|
||||||
|
n.Primary_ = nil
|
||||||
|
case *Edge:
|
||||||
|
n.Primary_ = nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if i == 0 && len(s.Value) == 1 {
|
||||||
|
node.Primary().Value = resolvedField.Primary().Value
|
||||||
|
} else {
|
||||||
|
s.Value[i].String = go2.Pointer(resolvedField.Primary().Value.ScalarString())
|
||||||
|
subbed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if resolvedField.Composite != nil {
|
||||||
|
switch n := node.(type) {
|
||||||
|
case *Field:
|
||||||
|
n.Composite = resolvedField.Composite
|
||||||
|
case *Edge:
|
||||||
|
if resolvedField.Composite.Map() == nil {
|
||||||
|
c.errorf(node.LastRef().AST(), `cannot substitute array variable "%s" to an edge`, strings.Join(box.Substitution.IDA(), "."))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
n.Map_ = resolvedField.Composite.Map()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if subbed {
|
||||||
|
s.Coalesce()
|
||||||
|
}
|
||||||
|
case *d2ast.DoubleQuotedString:
|
||||||
|
for i, box := range s.Value {
|
||||||
|
if box.Substitution != nil {
|
||||||
|
for _, vars := range varsStack {
|
||||||
|
resolvedField = c.resolveSubstitution(vars, box.Substitution)
|
||||||
|
if resolvedField != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if resolvedField == nil {
|
||||||
|
c.errorf(node.LastRef().AST(), `could not resolve variable "%s"`, strings.Join(box.Substitution.IDA(), "."))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if resolvedField.Primary() == nil && resolvedField.Composite != nil {
|
||||||
|
c.errorf(node.LastRef().AST(), `cannot substitute map variable "%s" in quotes`, strings.Join(box.Substitution.IDA(), "."))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.Value[i].String = go2.Pointer(resolvedField.Primary().Value.ScalarString())
|
||||||
|
subbed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if subbed {
|
||||||
|
s.Coalesce()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compiler) resolveSubstitution(vars *Map, substitution *d2ast.Substitution) *Field {
|
||||||
|
if vars == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, p := range substitution.Path {
|
||||||
|
f := vars.GetField(p.Unbox().ScalarString())
|
||||||
|
if f == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == len(substitution.Path)-1 {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
vars = f.Map()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *compiler) overlay(base *Map, f *Field) {
|
func (c *compiler) overlay(base *Map, f *Field) {
|
||||||
if f.Map() == nil || f.Primary() != nil {
|
if f.Map() == nil || f.Primary() != nil {
|
||||||
c.errorf(f.References[0].Context.Key, "invalid %s", NodeBoardKind(f))
|
c.errorf(f.References[0].Context.Key, "invalid %s", NodeBoardKind(f))
|
||||||
|
|
@ -107,6 +346,20 @@ func (c *compiler) overlay(base *Map, f *Field) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compiler) compileMap(dst *Map, ast, scopeAST *d2ast.Map) {
|
func (c *compiler) compileMap(dst *Map, ast, scopeAST *d2ast.Map) {
|
||||||
|
for _, n := range ast.Nodes {
|
||||||
|
switch {
|
||||||
|
case n.MapKey != nil:
|
||||||
|
ok := c.ampersandFilter(&RefContext{
|
||||||
|
Key: n.MapKey,
|
||||||
|
Scope: ast,
|
||||||
|
ScopeMap: dst,
|
||||||
|
ScopeAST: scopeAST,
|
||||||
|
})
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for _, n := range ast.Nodes {
|
for _, n := range ast.Nodes {
|
||||||
switch {
|
switch {
|
||||||
case n.MapKey != nil:
|
case n.MapKey != nil:
|
||||||
|
|
@ -116,6 +369,17 @@ func (c *compiler) compileMap(dst *Map, ast, scopeAST *d2ast.Map) {
|
||||||
ScopeMap: dst,
|
ScopeMap: dst,
|
||||||
ScopeAST: scopeAST,
|
ScopeAST: scopeAST,
|
||||||
})
|
})
|
||||||
|
case n.Substitution != nil:
|
||||||
|
// placeholder field to be resolved at the end
|
||||||
|
f := &Field{
|
||||||
|
parent: dst,
|
||||||
|
Primary_: &Scalar{
|
||||||
|
Value: &d2ast.UnquotedString{
|
||||||
|
Value: []d2ast.InterpolationBox{{Substitution: n.Substitution}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
dst.Fields = append(dst.Fields, f)
|
||||||
case n.Import != nil:
|
case n.Import != nil:
|
||||||
impn, ok := c._import(n.Import)
|
impn, ok := c._import(n.Import)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
@ -135,8 +399,6 @@ func (c *compiler) compileMap(dst *Map, ast, scopeAST *d2ast.Map) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case n.Substitution != nil:
|
|
||||||
panic("TODO")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -150,16 +412,87 @@ func (c *compiler) compileKey(refctx *RefContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compiler) compileField(dst *Map, kp *d2ast.KeyPath, refctx *RefContext) {
|
func (c *compiler) compileField(dst *Map, kp *d2ast.KeyPath, refctx *RefContext) {
|
||||||
if refctx.Key != nil && len(refctx.Key.Edges) == 0 && refctx.Key.Value.Null != nil {
|
if refctx.Key.Ampersand {
|
||||||
dst.DeleteField(kp.IDA()...)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
f, err := dst.EnsureField(kp, refctx)
|
|
||||||
|
fa, err := dst.EnsureField(kp, refctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
|
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, f := range fa {
|
||||||
|
c._compileField(f, refctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compiler) ampersandFilter(refctx *RefContext) bool {
|
||||||
|
if !refctx.Key.Ampersand {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if len(c.globStack) == 0 || !c.globStack[len(c.globStack)-1] {
|
||||||
|
c.errorf(refctx.Key, "glob filters cannot be used outside globs")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(refctx.Key.Edges) > 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fa, err := refctx.ScopeMap.EnsureField(refctx.Key.Key, refctx, false)
|
||||||
|
if err != nil {
|
||||||
|
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(fa) == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, f := range fa {
|
||||||
|
ok := c._ampersandFilter(f, refctx)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compiler) _ampersandFilter(f *Field, refctx *RefContext) bool {
|
||||||
|
if refctx.Key.Value.ScalarBox().Unbox() == nil {
|
||||||
|
c.errorf(refctx.Key, "glob filters cannot be composites")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if a, ok := f.Composite.(*Array); ok {
|
||||||
|
for _, v := range a.Values {
|
||||||
|
if s, ok := v.(*Scalar); ok {
|
||||||
|
if refctx.Key.Value.ScalarBox().Unbox().ScalarString() == s.Value.ScalarString() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.Primary_ == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if refctx.Key.Value.ScalarBox().Unbox().ScalarString() != f.Primary_.Value.ScalarString() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compiler) _compileField(f *Field, refctx *RefContext) {
|
||||||
|
if len(refctx.Key.Edges) == 0 && refctx.Key.Value.Null != nil {
|
||||||
|
// For vars, if we delete the field, it may just resolve to an outer scope var of the same name
|
||||||
|
// Instead we keep it around, so that resolveSubstitutions can find it
|
||||||
|
if !IsVar(ParentMap(f)) {
|
||||||
|
ParentMap(f).DeleteField(f.Name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if refctx.Key.Primary.Unbox() != nil {
|
if refctx.Key.Primary.Unbox() != nil {
|
||||||
f.Primary_ = &Scalar{
|
f.Primary_ = &Scalar{
|
||||||
parent: f,
|
parent: f,
|
||||||
|
|
@ -199,10 +532,12 @@ func (c *compiler) compileField(dst *Map, kp *d2ast.KeyPath, refctx *RefContext)
|
||||||
// If new board type, use that as the new scope AST, otherwise, carry on
|
// If new board type, use that as the new scope AST, otherwise, carry on
|
||||||
scopeAST = refctx.ScopeAST
|
scopeAST = refctx.ScopeAST
|
||||||
}
|
}
|
||||||
|
c.globStack = append(c.globStack, refctx.Key.HasQueryGlob())
|
||||||
c.compileMap(f.Map(), refctx.Key.Value.Map, scopeAST)
|
c.compileMap(f.Map(), refctx.Key.Value.Map, scopeAST)
|
||||||
|
c.globStack = c.globStack[:len(c.globStack)-1]
|
||||||
switch NodeBoardKind(f) {
|
switch NodeBoardKind(f) {
|
||||||
case BoardScenario, BoardStep:
|
case BoardScenario, BoardStep:
|
||||||
c.compileClasses(f.Map())
|
c.overlayClasses(f.Map())
|
||||||
}
|
}
|
||||||
} else if refctx.Key.Value.Import != nil {
|
} else if refctx.Key.Value.Import != nil {
|
||||||
n, ok := c._import(refctx.Key.Value.Import)
|
n, ok := c._import(refctx.Key.Value.Import)
|
||||||
|
|
@ -241,7 +576,7 @@ func (c *compiler) compileField(dst *Map, kp *d2ast.KeyPath, refctx *RefContext)
|
||||||
c.updateLinks(f.Map())
|
c.updateLinks(f.Map())
|
||||||
switch NodeBoardKind(f) {
|
switch NodeBoardKind(f) {
|
||||||
case BoardScenario, BoardStep:
|
case BoardScenario, BoardStep:
|
||||||
c.compileClasses(f.Map())
|
c.overlayClasses(f.Map())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if refctx.Key.Value.ScalarBox().Unbox() != nil {
|
} else if refctx.Key.Value.ScalarBox().Unbox() != nil {
|
||||||
|
|
@ -259,6 +594,21 @@ func (c *compiler) compileField(dst *Map, kp *d2ast.KeyPath, refctx *RefContext)
|
||||||
func (c *compiler) updateLinks(m *Map) {
|
func (c *compiler) updateLinks(m *Map) {
|
||||||
for _, f := range m.Fields {
|
for _, f := range m.Fields {
|
||||||
if f.Name == "link" {
|
if f.Name == "link" {
|
||||||
|
val := f.Primary().Value.ScalarString()
|
||||||
|
link, err := d2parser.ParseKey(val)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
linkIDA := link.IDA()
|
||||||
|
if len(linkIDA) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// When updateLinks is called, all valid board links are already compiled and changed to the qualified path beginning with "root"
|
||||||
|
if linkIDA[0] != "root" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
bida := BoardIDA(f)
|
bida := BoardIDA(f)
|
||||||
aida := IDA(f)
|
aida := IDA(f)
|
||||||
if len(bida) != len(aida) {
|
if len(bida) != len(aida) {
|
||||||
|
|
@ -337,12 +687,17 @@ func (c *compiler) compileLink(refctx *RefContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compiler) compileEdges(refctx *RefContext) {
|
func (c *compiler) compileEdges(refctx *RefContext) {
|
||||||
if refctx.Key.Key != nil {
|
if refctx.Key.Key == nil {
|
||||||
f, err := refctx.ScopeMap.EnsureField(refctx.Key.Key, refctx)
|
c._compileEdges(refctx)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fa, err := refctx.ScopeMap.EnsureField(refctx.Key.Key, refctx, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
|
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
for _, f := range fa {
|
||||||
if _, ok := f.Composite.(*Array); ok {
|
if _, ok := f.Composite.(*Array); ok {
|
||||||
c.errorf(refctx.Key.Key, "cannot index into array")
|
c.errorf(refctx.Key.Key, "cannot index into array")
|
||||||
return
|
return
|
||||||
|
|
@ -352,9 +707,13 @@ func (c *compiler) compileEdges(refctx *RefContext) {
|
||||||
parent: f,
|
parent: f,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refctx.ScopeMap = f.Map()
|
refctx2 := *refctx
|
||||||
|
refctx2.ScopeMap = f.Map()
|
||||||
|
c._compileEdges(&refctx2)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *compiler) _compileEdges(refctx *RefContext) {
|
||||||
eida := NewEdgeIDs(refctx.Key)
|
eida := NewEdgeIDs(refctx.Key)
|
||||||
for i, eid := range eida {
|
for i, eid := range eida {
|
||||||
if refctx.Key != nil && refctx.Key.Value.Null != nil {
|
if refctx.Key != nil && refctx.Key.Value.Null != nil {
|
||||||
|
|
@ -365,38 +724,30 @@ func (c *compiler) compileEdges(refctx *RefContext) {
|
||||||
refctx = refctx.Copy()
|
refctx = refctx.Copy()
|
||||||
refctx.Edge = refctx.Key.Edges[i]
|
refctx.Edge = refctx.Key.Edges[i]
|
||||||
|
|
||||||
var e *Edge
|
var ea []*Edge
|
||||||
if eid.Index != nil {
|
if eid.Index != nil || eid.Glob {
|
||||||
ea := refctx.ScopeMap.GetEdges(eid)
|
ea = refctx.ScopeMap.GetEdges(eid, refctx)
|
||||||
if len(ea) == 0 {
|
if len(ea) == 0 {
|
||||||
c.errorf(refctx.Edge, "indexed edge does not exist")
|
c.errorf(refctx.Edge, "indexed edge does not exist")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
e = ea[0]
|
for _, e := range ea {
|
||||||
e.References = append(e.References, &EdgeReference{
|
e.References = append(e.References, &EdgeReference{
|
||||||
Context: refctx,
|
Context: refctx,
|
||||||
})
|
})
|
||||||
refctx.ScopeMap.appendFieldReferences(0, refctx.Edge.Src, refctx)
|
refctx.ScopeMap.appendFieldReferences(0, refctx.Edge.Src, refctx)
|
||||||
refctx.ScopeMap.appendFieldReferences(0, refctx.Edge.Dst, refctx)
|
refctx.ScopeMap.appendFieldReferences(0, refctx.Edge.Dst, refctx)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err := refctx.ScopeMap.EnsureField(refctx.Edge.Src, refctx)
|
var err error
|
||||||
if err != nil {
|
ea, err = refctx.ScopeMap.CreateEdge(eid, refctx)
|
||||||
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
_, err = refctx.ScopeMap.EnsureField(refctx.Edge.Dst, refctx)
|
|
||||||
if err != nil {
|
|
||||||
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
e, err = refctx.ScopeMap.CreateEdge(eid, refctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
|
c.err.Errors = append(c.err.Errors, err.(d2ast.Error))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, e := range ea {
|
||||||
if refctx.Key.EdgeKey != nil {
|
if refctx.Key.EdgeKey != nil {
|
||||||
if e.Map_ == nil {
|
if e.Map_ == nil {
|
||||||
e.Map_ = &Map{
|
e.Map_ = &Map{
|
||||||
|
|
@ -420,7 +771,9 @@ func (c *compiler) compileEdges(refctx *RefContext) {
|
||||||
parent: e,
|
parent: e,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
c.globStack = append(c.globStack, refctx.Key.HasQueryGlob())
|
||||||
c.compileMap(e.Map_, refctx.Key.Value.Map, refctx.ScopeAST)
|
c.compileMap(e.Map_, refctx.Key.Value.Map, refctx.ScopeAST)
|
||||||
|
c.globStack = c.globStack[:len(c.globStack)-1]
|
||||||
} else if refctx.Key.Value.ScalarBox().Unbox() != nil {
|
} else if refctx.Key.Value.ScalarBox().Unbox() != nil {
|
||||||
e.Primary_ = &Scalar{
|
e.Primary_ = &Scalar{
|
||||||
parent: e,
|
parent: e,
|
||||||
|
|
@ -429,6 +782,7 @@ func (c *compiler) compileEdges(refctx *RefContext) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compiler) compileArray(dst *Array, a *d2ast.Array, scopeAST *d2ast.Map) {
|
func (c *compiler) compileArray(dst *Array, a *d2ast.Array, scopeAST *d2ast.Map) {
|
||||||
|
|
@ -481,7 +835,12 @@ func (c *compiler) compileArray(dst *Array, a *d2ast.Array, scopeAST *d2ast.Map)
|
||||||
irv = n
|
irv = n
|
||||||
}
|
}
|
||||||
case *d2ast.Substitution:
|
case *d2ast.Substitution:
|
||||||
// panic("TODO")
|
irv = &Scalar{
|
||||||
|
parent: dst,
|
||||||
|
Value: &d2ast.UnquotedString{
|
||||||
|
Value: []d2ast.InterpolationBox{{Substitution: an.Substitution}},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dst.Values = append(dst.Values, irv)
|
dst.Values = append(dst.Values, irv)
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,8 @@ func TestCompile(t *testing.T) {
|
||||||
t.Run("scenarios", testCompileScenarios)
|
t.Run("scenarios", testCompileScenarios)
|
||||||
t.Run("steps", testCompileSteps)
|
t.Run("steps", testCompileSteps)
|
||||||
t.Run("imports", testCompileImports)
|
t.Run("imports", testCompileImports)
|
||||||
|
t.Run("patterns", testCompilePatterns)
|
||||||
|
t.Run("filters", testCompileFilters)
|
||||||
}
|
}
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
|
|
@ -84,23 +86,31 @@ func assertQuery(t testing.TB, n d2ir.Node, nfields, nedges int, primary interfa
|
||||||
m := n.Map()
|
m := n.Map()
|
||||||
p := n.Primary()
|
p := n.Primary()
|
||||||
|
|
||||||
|
var na []d2ir.Node
|
||||||
if idStr != "" {
|
if idStr != "" {
|
||||||
var err error
|
var err error
|
||||||
n, err = m.Query(idStr)
|
na, err = m.QueryAll(idStr)
|
||||||
assert.Success(t, err)
|
assert.Success(t, err)
|
||||||
assert.NotEqual(t, n, nil)
|
assert.NotEqual(t, n, nil)
|
||||||
|
} else {
|
||||||
p = n.Primary()
|
na = append(na, n)
|
||||||
m = n.Map()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, n := range na {
|
||||||
|
m = n.Map()
|
||||||
|
p = n.Primary()
|
||||||
assert.Equal(t, nfields, m.FieldCountRecursive())
|
assert.Equal(t, nfields, m.FieldCountRecursive())
|
||||||
assert.Equal(t, nedges, m.EdgeCountRecursive())
|
assert.Equal(t, nedges, m.EdgeCountRecursive())
|
||||||
if !makeScalar(p).Equal(makeScalar(primary)) {
|
if !makeScalar(p).Equal(makeScalar(primary)) {
|
||||||
t.Fatalf("expected primary %#v but got %s", primary, p)
|
t.Fatalf("expected primary %#v but got %s", primary, p)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return n
|
if len(na) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return na[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeScalar(v interface{}) *d2ir.Scalar {
|
func makeScalar(v interface{}) *d2ir.Scalar {
|
||||||
|
|
|
||||||
310
d2ir/d2ir.go
|
|
@ -325,6 +325,7 @@ type EdgeID struct {
|
||||||
|
|
||||||
// If nil, then any EdgeID with equal src/dst/arrows matches.
|
// If nil, then any EdgeID with equal src/dst/arrows matches.
|
||||||
Index *int `json:"index"`
|
Index *int `json:"index"`
|
||||||
|
Glob bool `json:"glob"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewEdgeIDs(k *d2ast.Key) (eida []*EdgeID) {
|
func NewEdgeIDs(k *d2ast.Key) (eida []*EdgeID) {
|
||||||
|
|
@ -337,6 +338,7 @@ func NewEdgeIDs(k *d2ast.Key) (eida []*EdgeID) {
|
||||||
}
|
}
|
||||||
if k.EdgeIndex != nil {
|
if k.EdgeIndex != nil {
|
||||||
eid.Index = k.EdgeIndex.Int
|
eid.Index = k.EdgeIndex.Int
|
||||||
|
eid.Glob = k.EdgeIndex.Glob
|
||||||
}
|
}
|
||||||
eida = append(eida, eid)
|
eida = append(eida, eid)
|
||||||
}
|
}
|
||||||
|
|
@ -585,6 +587,19 @@ func (m *Map) FieldCountRecursive() int {
|
||||||
return acc
|
return acc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Map) IsContainer() bool {
|
||||||
|
if m == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, f := range m.Fields {
|
||||||
|
_, isReserved := d2graph.ReservedKeywords[f.Name]
|
||||||
|
if !isReserved {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Map) EdgeCountRecursive() int {
|
func (m *Map) EdgeCountRecursive() int {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -651,7 +666,8 @@ func (m *Map) getField(ida []string) *Field {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Map) EnsureField(kp *d2ast.KeyPath, refctx *RefContext) (*Field, error) {
|
// EnsureField is a bit of a misnomer. It's more of a Query/Ensure combination function at this point.
|
||||||
|
func (m *Map) EnsureField(kp *d2ast.KeyPath, refctx *RefContext, create bool) ([]*Field, error) {
|
||||||
i := 0
|
i := 0
|
||||||
for kp.Path[i].Unbox().ScalarString() == "_" {
|
for kp.Path[i].Unbox().ScalarString() == "_" {
|
||||||
m = ParentMap(m)
|
m = ParentMap(m)
|
||||||
|
|
@ -663,29 +679,73 @@ func (m *Map) EnsureField(kp *d2ast.KeyPath, refctx *RefContext) (*Field, error)
|
||||||
}
|
}
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
return m.ensureField(i, kp, refctx)
|
|
||||||
|
var fa []*Field
|
||||||
|
err := m.ensureField(i, kp, refctx, create, &fa)
|
||||||
|
return fa, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext) (*Field, error) {
|
func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext, create bool, fa *[]*Field) error {
|
||||||
|
us, ok := kp.Path[i].Unbox().(*d2ast.UnquotedString)
|
||||||
|
if ok && us.Pattern != nil {
|
||||||
|
fa2, ok := m.doubleGlob(us.Pattern)
|
||||||
|
if ok {
|
||||||
|
if i == len(kp.Path)-1 {
|
||||||
|
*fa = append(*fa, fa2...)
|
||||||
|
} else {
|
||||||
|
for _, f := range fa2 {
|
||||||
|
if f.Map() == nil {
|
||||||
|
f.Composite = &Map{
|
||||||
|
parent: f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := f.Map().ensureField(i+1, kp, refctx, create, fa)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, f := range m.Fields {
|
||||||
|
if matchPattern(f.Name, us.Pattern) {
|
||||||
|
if i == len(kp.Path)-1 {
|
||||||
|
*fa = append(*fa, f)
|
||||||
|
} else {
|
||||||
|
if f.Map() == nil {
|
||||||
|
f.Composite = &Map{
|
||||||
|
parent: f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err := f.Map().ensureField(i+1, kp, refctx, create, fa)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
head := kp.Path[i].Unbox().ScalarString()
|
head := kp.Path[i].Unbox().ScalarString()
|
||||||
|
|
||||||
if _, ok := d2graph.ReservedKeywords[strings.ToLower(head)]; ok {
|
if _, ok := d2graph.ReservedKeywords[strings.ToLower(head)]; ok {
|
||||||
head = strings.ToLower(head)
|
head = strings.ToLower(head)
|
||||||
if _, ok := d2graph.CompositeReservedKeywords[head]; !ok && i < len(kp.Path)-1 {
|
if _, ok := d2graph.CompositeReservedKeywords[head]; !ok && i < len(kp.Path)-1 {
|
||||||
return nil, d2parser.Errorf(kp.Path[i].Unbox(), fmt.Sprintf(`"%s" must be the last part of the key`, head))
|
return d2parser.Errorf(kp.Path[i].Unbox(), fmt.Sprintf(`"%s" must be the last part of the key`, head))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if head == "_" {
|
if head == "_" {
|
||||||
return nil, d2parser.Errorf(kp.Path[i].Unbox(), `parent "_" can only be used in the beginning of paths, e.g. "_.x"`)
|
return d2parser.Errorf(kp.Path[i].Unbox(), `parent "_" can only be used in the beginning of paths, e.g. "_.x"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if head == "classes" && NodeBoardKind(m) == "" {
|
if head == "classes" && NodeBoardKind(m) == "" {
|
||||||
return nil, d2parser.Errorf(kp.Path[i].Unbox(), "%s is only allowed at a board root", head)
|
return d2parser.Errorf(kp.Path[i].Unbox(), "%s is only allowed at a board root", head)
|
||||||
}
|
}
|
||||||
|
|
||||||
if findBoardKeyword(head) != -1 && NodeBoardKind(m) == "" {
|
if findBoardKeyword(head) != -1 && NodeBoardKind(m) == "" {
|
||||||
return nil, d2parser.Errorf(kp.Path[i].Unbox(), "%s is only allowed at a board root", head)
|
return d2parser.Errorf(kp.Path[i].Unbox(), "%s is only allowed at a board root", head)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range m.Fields {
|
for _, f := range m.Fields {
|
||||||
|
|
@ -703,19 +763,23 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext) (*Field,
|
||||||
}
|
}
|
||||||
|
|
||||||
if i+1 == len(kp.Path) {
|
if i+1 == len(kp.Path) {
|
||||||
return f, nil
|
*fa = append(*fa, f)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if _, ok := f.Composite.(*Array); ok {
|
if _, ok := f.Composite.(*Array); ok {
|
||||||
return nil, d2parser.Errorf(kp.Path[i].Unbox(), "cannot index into array")
|
return d2parser.Errorf(kp.Path[i].Unbox(), "cannot index into array")
|
||||||
}
|
}
|
||||||
if f.Map() == nil {
|
if f.Map() == nil {
|
||||||
f.Composite = &Map{
|
f.Composite = &Map{
|
||||||
parent: f,
|
parent: f,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return f.Map().ensureField(i+1, kp, refctx)
|
return f.Map().ensureField(i+1, kp, refctx, create, fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !create {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
f := &Field{
|
f := &Field{
|
||||||
parent: m,
|
parent: m,
|
||||||
Name: head,
|
Name: head,
|
||||||
|
|
@ -730,12 +794,13 @@ func (m *Map) ensureField(i int, kp *d2ast.KeyPath, refctx *RefContext) (*Field,
|
||||||
}
|
}
|
||||||
m.Fields = append(m.Fields, f)
|
m.Fields = append(m.Fields, f)
|
||||||
if i+1 == len(kp.Path) {
|
if i+1 == len(kp.Path) {
|
||||||
return f, nil
|
*fa = append(*fa, f)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
f.Composite = &Map{
|
f.Composite = &Map{
|
||||||
parent: f,
|
parent: f,
|
||||||
}
|
}
|
||||||
return f.Map().ensureField(i+1, kp, refctx)
|
return f.Map().ensureField(i+1, kp, refctx, create, fa)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Map) DeleteEdge(eid *EdgeID) *Edge {
|
func (m *Map) DeleteEdge(eid *EdgeID) *Edge {
|
||||||
|
|
@ -800,7 +865,13 @@ func (m *Map) DeleteField(ida ...string) *Field {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Map) GetEdges(eid *EdgeID) []*Edge {
|
func (m *Map) GetEdges(eid *EdgeID, refctx *RefContext) []*Edge {
|
||||||
|
if refctx != nil {
|
||||||
|
var ea []*Edge
|
||||||
|
m.getEdges(eid, refctx, &ea)
|
||||||
|
return ea
|
||||||
|
}
|
||||||
|
|
||||||
eid, m, common, err := eid.resolve(m)
|
eid, m, common, err := eid.resolve(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -811,7 +882,7 @@ func (m *Map) GetEdges(eid *EdgeID) []*Edge {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if f.Map() != nil {
|
if f.Map() != nil {
|
||||||
return f.Map().GetEdges(eid)
|
return f.Map().GetEdges(eid, nil)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -825,65 +896,197 @@ func (m *Map) GetEdges(eid *EdgeID) []*Edge {
|
||||||
return ea
|
return ea
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Map) CreateEdge(eid *EdgeID, refctx *RefContext) (*Edge, error) {
|
func (m *Map) getEdges(eid *EdgeID, refctx *RefContext, ea *[]*Edge) error {
|
||||||
if ParentEdge(m) != nil {
|
|
||||||
return nil, d2parser.Errorf(refctx.Edge, "cannot create edge inside edge")
|
|
||||||
}
|
|
||||||
|
|
||||||
eid, m, common, err := eid.resolve(m)
|
eid, m, common, err := eid.resolve(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, d2parser.Errorf(refctx.Edge, err.Error())
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(common) > 0 {
|
if len(common) > 0 {
|
||||||
f, err := m.EnsureField(d2ast.MakeKeyPath(common), nil)
|
commonKP := d2ast.MakeKeyPath(common)
|
||||||
if err != nil {
|
lastMatch := 0
|
||||||
return nil, err
|
for i, el := range commonKP.Path {
|
||||||
|
for j := lastMatch; j < len(refctx.Edge.Src.Path); j++ {
|
||||||
|
realEl := refctx.Edge.Src.Path[j]
|
||||||
|
if el.ScalarString() == realEl.ScalarString() {
|
||||||
|
commonKP.Path[i] = realEl
|
||||||
|
lastMatch += j + 1
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fa, err := m.EnsureField(commonKP, nil, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, f := range fa {
|
||||||
if _, ok := f.Composite.(*Array); ok {
|
if _, ok := f.Composite.(*Array); ok {
|
||||||
return nil, d2parser.Errorf(refctx.Edge.Src, "cannot index into array")
|
return d2parser.Errorf(refctx.Edge.Src, "cannot index into array")
|
||||||
}
|
}
|
||||||
if f.Map() == nil {
|
if f.Map() == nil {
|
||||||
f.Composite = &Map{
|
f.Composite = &Map{
|
||||||
parent: f,
|
parent: f,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return f.Map().CreateEdge(eid, refctx)
|
err = f.Map().getEdges(eid, refctx, ea)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
srcFA, err := refctx.ScopeMap.EnsureField(refctx.Edge.Src, nil, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dstFA, err := refctx.ScopeMap.EnsureField(refctx.Edge.Dst, nil, false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, src := range srcFA {
|
||||||
|
for _, dst := range dstFA {
|
||||||
|
eid2 := eid.Copy()
|
||||||
|
eid2.SrcPath = RelIDA(m, src)
|
||||||
|
eid2.DstPath = RelIDA(m, dst)
|
||||||
|
|
||||||
|
ea2 := m.GetEdges(eid2, nil)
|
||||||
|
*ea = append(*ea, ea2...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map) CreateEdge(eid *EdgeID, refctx *RefContext) ([]*Edge, error) {
|
||||||
|
var ea []*Edge
|
||||||
|
return ea, m.createEdge(eid, refctx, &ea)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map) createEdge(eid *EdgeID, refctx *RefContext, ea *[]*Edge) error {
|
||||||
|
if ParentEdge(m) != nil {
|
||||||
|
return d2parser.Errorf(refctx.Edge, "cannot create edge inside edge")
|
||||||
|
}
|
||||||
|
|
||||||
|
eid, m, common, err := eid.resolve(m)
|
||||||
|
if err != nil {
|
||||||
|
return d2parser.Errorf(refctx.Edge, err.Error())
|
||||||
|
}
|
||||||
|
if len(common) > 0 {
|
||||||
|
commonKP := d2ast.MakeKeyPath(common)
|
||||||
|
lastMatch := 0
|
||||||
|
for i, el := range commonKP.Path {
|
||||||
|
for j := lastMatch; j < len(refctx.Edge.Src.Path); j++ {
|
||||||
|
realEl := refctx.Edge.Src.Path[j]
|
||||||
|
if el.ScalarString() == realEl.ScalarString() {
|
||||||
|
commonKP.Path[i] = realEl
|
||||||
|
lastMatch += j + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fa, err := m.EnsureField(commonKP, nil, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, f := range fa {
|
||||||
|
if _, ok := f.Composite.(*Array); ok {
|
||||||
|
return d2parser.Errorf(refctx.Edge.Src, "cannot index into array")
|
||||||
|
}
|
||||||
|
if f.Map() == nil {
|
||||||
|
f.Composite = &Map{
|
||||||
|
parent: f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
err = f.Map().createEdge(eid, refctx, ea)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
ij := findProhibitedEdgeKeyword(eid.SrcPath...)
|
ij := findProhibitedEdgeKeyword(eid.SrcPath...)
|
||||||
if ij != -1 {
|
if ij != -1 {
|
||||||
return nil, d2parser.Errorf(refctx.Edge.Src.Path[ij].Unbox(), "reserved keywords are prohibited in edges")
|
return d2parser.Errorf(refctx.Edge.Src.Path[ij].Unbox(), "reserved keywords are prohibited in edges")
|
||||||
}
|
}
|
||||||
ij = findBoardKeyword(eid.SrcPath...)
|
ij = findBoardKeyword(eid.SrcPath...)
|
||||||
if ij == len(eid.SrcPath)-1 {
|
if ij == len(eid.SrcPath)-1 {
|
||||||
return nil, d2parser.Errorf(refctx.Edge.Src.Path[ij].Unbox(), "edge with board keyword alone doesn't make sense")
|
return d2parser.Errorf(refctx.Edge.Src.Path[ij].Unbox(), "edge with board keyword alone doesn't make sense")
|
||||||
}
|
|
||||||
src := m.GetField(eid.SrcPath...)
|
|
||||||
if NodeBoardKind(src) != "" {
|
|
||||||
return nil, d2parser.Errorf(refctx.Edge.Src, "cannot create edges between boards")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ij = findProhibitedEdgeKeyword(eid.DstPath...)
|
ij = findProhibitedEdgeKeyword(eid.DstPath...)
|
||||||
if ij != -1 {
|
if ij != -1 {
|
||||||
return nil, d2parser.Errorf(refctx.Edge.Dst.Path[ij].Unbox(), "reserved keywords are prohibited in edges")
|
return d2parser.Errorf(refctx.Edge.Dst.Path[ij].Unbox(), "reserved keywords are prohibited in edges")
|
||||||
}
|
}
|
||||||
ij = findBoardKeyword(eid.DstPath...)
|
ij = findBoardKeyword(eid.DstPath...)
|
||||||
if ij == len(eid.DstPath)-1 {
|
if ij == len(eid.DstPath)-1 {
|
||||||
return nil, d2parser.Errorf(refctx.Edge.Dst.Path[ij].Unbox(), "edge with board keyword alone doesn't make sense")
|
return d2parser.Errorf(refctx.Edge.Dst.Path[ij].Unbox(), "edge with board keyword alone doesn't make sense")
|
||||||
|
}
|
||||||
|
|
||||||
|
srcFA, err := refctx.ScopeMap.EnsureField(refctx.Edge.Src, refctx, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dstFA, err := refctx.ScopeMap.EnsureField(refctx.Edge.Dst, refctx, true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, src := range srcFA {
|
||||||
|
for _, dst := range dstFA {
|
||||||
|
if src == dst && (refctx.Edge.Src.HasGlob() || refctx.Edge.Dst.HasGlob()) {
|
||||||
|
// Globs do not make self edges.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if refctx.Edge.Src.HasDoubleGlob() {
|
||||||
|
// If src has a double glob we only select leafs, those without children.
|
||||||
|
if src.Map().IsContainer() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ParentBoard(src) != ParentBoard(dst) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if refctx.Edge.Dst.HasDoubleGlob() {
|
||||||
|
// If dst has a double glob we only select leafs, those without children.
|
||||||
|
if dst.Map().IsContainer() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if ParentBoard(src) != ParentBoard(dst) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eid2 := eid.Copy()
|
||||||
|
eid2.SrcPath = RelIDA(m, src)
|
||||||
|
eid2.DstPath = RelIDA(m, dst)
|
||||||
|
e, err := m.createEdge2(eid2, refctx, src, dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*ea = append(*ea, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map) createEdge2(eid *EdgeID, refctx *RefContext, src, dst *Field) (*Edge, error) {
|
||||||
|
if NodeBoardKind(src) != "" {
|
||||||
|
return nil, d2parser.Errorf(refctx.Edge.Src, "cannot create edges between boards")
|
||||||
}
|
}
|
||||||
dst := m.GetField(eid.DstPath...)
|
|
||||||
if NodeBoardKind(dst) != "" {
|
if NodeBoardKind(dst) != "" {
|
||||||
return nil, d2parser.Errorf(refctx.Edge.Dst, "cannot create edges between boards")
|
return nil, d2parser.Errorf(refctx.Edge.Dst, "cannot create edges between boards")
|
||||||
}
|
}
|
||||||
|
|
||||||
if ParentBoard(src) != ParentBoard(dst) {
|
if ParentBoard(src) != ParentBoard(dst) {
|
||||||
return nil, d2parser.Errorf(refctx.Edge, "cannot create edges between boards")
|
return nil, d2parser.Errorf(refctx.Edge, "cannot create edges between boards")
|
||||||
}
|
}
|
||||||
|
|
||||||
eid.Index = nil
|
eid.Index = nil
|
||||||
ea := m.GetEdges(eid)
|
eid.Glob = true
|
||||||
|
ea := m.GetEdges(eid, nil)
|
||||||
index := len(ea)
|
index := len(ea)
|
||||||
eid.Index = &index
|
eid.Index = &index
|
||||||
|
eid.Glob = false
|
||||||
e := &Edge{
|
e := &Edge{
|
||||||
parent: m,
|
parent: m,
|
||||||
ID: eid,
|
ID: eid,
|
||||||
|
|
@ -1026,6 +1229,21 @@ func ParentField(n Node) *Field {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsVar(n Node) bool {
|
||||||
|
for {
|
||||||
|
if n == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if NodeBoardKind(n) != "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if f, ok := n.(*Field); ok && f.Name == "vars" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
n = n.Parent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ParentBoard(n Node) Node {
|
func ParentBoard(n Node) Node {
|
||||||
for {
|
for {
|
||||||
n = n.Parent()
|
n = n.Parent()
|
||||||
|
|
@ -1144,6 +1362,26 @@ func IDA(n Node) (ida []string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RelIDA returns the path to n relative to p.
|
||||||
|
func RelIDA(p, n Node) (ida []string) {
|
||||||
|
for {
|
||||||
|
f, ok := n.(*Field)
|
||||||
|
if ok {
|
||||||
|
ida = append(ida, f.Name)
|
||||||
|
if f.Root() {
|
||||||
|
reverseIDA(ida)
|
||||||
|
return ida
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f = ParentField(n)
|
||||||
|
if f == nil || f.Root() || f == p || f.Composite == p {
|
||||||
|
reverseIDA(ida)
|
||||||
|
return ida
|
||||||
|
}
|
||||||
|
n = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func reverseIDA(ida []string) {
|
func reverseIDA(ida []string) {
|
||||||
for i := 0; i < len(ida)/2; i++ {
|
for i := 0; i < len(ida)/2; i++ {
|
||||||
tmp := ida[i]
|
tmp := ida[i]
|
||||||
|
|
|
||||||
158
d2ir/filter_test.go
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
package d2ir_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/util-go/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testCompileFilters(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tca := []testCase{
|
||||||
|
{
|
||||||
|
name: "base",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `jacob: {
|
||||||
|
shape: circle
|
||||||
|
}
|
||||||
|
jeremy: {
|
||||||
|
shape: rectangle
|
||||||
|
}
|
||||||
|
*: {
|
||||||
|
&shape: rectangle
|
||||||
|
label: I'm a rectangle
|
||||||
|
}`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 1, 0, nil, "jacob")
|
||||||
|
assertQuery(t, m, 2, 0, nil, "jeremy")
|
||||||
|
assertQuery(t, m, 0, 0, "I'm a rectangle", "jeremy.label")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "order",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `jacob: {
|
||||||
|
shape: circle
|
||||||
|
}
|
||||||
|
jeremy: {
|
||||||
|
shape: rectangle
|
||||||
|
}
|
||||||
|
*: {
|
||||||
|
label: I'm a rectangle
|
||||||
|
&shape: rectangle
|
||||||
|
}`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 5, 0, nil, "")
|
||||||
|
assertQuery(t, m, 1, 0, nil, "jacob")
|
||||||
|
assertQuery(t, m, 2, 0, nil, "jeremy")
|
||||||
|
assertQuery(t, m, 0, 0, "I'm a rectangle", "jeremy.label")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "array",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `the-little-cannon: {
|
||||||
|
class: [server; deployed]
|
||||||
|
}
|
||||||
|
dino: {
|
||||||
|
class: [internal; deployed]
|
||||||
|
}
|
||||||
|
catapult: {
|
||||||
|
class: [jacob; server]
|
||||||
|
}
|
||||||
|
|
||||||
|
*: {
|
||||||
|
&class: server
|
||||||
|
style.multiple: true
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 10, 0, nil, "")
|
||||||
|
assertQuery(t, m, 3, 0, nil, "the-little-cannon")
|
||||||
|
assertQuery(t, m, 1, 0, nil, "dino")
|
||||||
|
assertQuery(t, m, 3, 0, nil, "catapult")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "edge",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `x -> y: {
|
||||||
|
source-arrowhead.shape: diamond
|
||||||
|
target-arrowhead.shape: diamond
|
||||||
|
}
|
||||||
|
x -> y
|
||||||
|
|
||||||
|
(x -> *)[*]: {
|
||||||
|
&source-arrowhead.shape: diamond
|
||||||
|
&target-arrowhead.shape: diamond
|
||||||
|
label: diamond shape arrowheads
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 7, 2, nil, "")
|
||||||
|
assertQuery(t, m, 5, 0, nil, "(x -> y)[0]")
|
||||||
|
assertQuery(t, m, 0, 0, "diamond shape arrowheads", "(x -> y)[0].label")
|
||||||
|
assertQuery(t, m, 0, 0, nil, "(x -> y)[1]")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
runa(t, tca)
|
||||||
|
|
||||||
|
t.Run("errors", func(t *testing.T) {
|
||||||
|
tca := []testCase{
|
||||||
|
{
|
||||||
|
name: "bad-syntax",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
_, err := compile(t, `jacob.style: {
|
||||||
|
fill: red
|
||||||
|
multiple: true
|
||||||
|
}
|
||||||
|
|
||||||
|
*.&style: {
|
||||||
|
fill: red
|
||||||
|
multiple: true
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
assert.ErrorString(t, err, `TestCompile/filters/errors/bad-syntax.d2:6:3: unexpected text after map key
|
||||||
|
TestCompile/filters/errors/bad-syntax.d2:9:1: unexpected map termination character } in file map`)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no-glob",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
_, err := compile(t, `jacob.style: {
|
||||||
|
fill: red
|
||||||
|
multiple: true
|
||||||
|
}
|
||||||
|
|
||||||
|
jasmine.style: {
|
||||||
|
&fill: red
|
||||||
|
multiple: false
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
assert.ErrorString(t, err, `TestCompile/filters/errors/no-glob.d2:7:3: glob filters cannot be used outside globs`)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "composite",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
_, err := compile(t, `jacob.style: {
|
||||||
|
fill: red
|
||||||
|
multiple: true
|
||||||
|
}
|
||||||
|
*: {
|
||||||
|
&style: {
|
||||||
|
fill: red
|
||||||
|
multiple: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`)
|
||||||
|
assert.ErrorString(t, err, `TestCompile/filters/errors/composite.d2:6:2: glob filters cannot be composites`)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
runa(t, tca)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -138,6 +138,39 @@ label: meow`,
|
||||||
assertQuery(t, m, 0, 0, nil, "q.jon")
|
assertQuery(t, m, 0, 0, nil, "q.jon")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "vars/1",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compileFS(t, "index.d2", map[string]string{
|
||||||
|
"index.d2": "vars: { ...@x }; q: ${meow}",
|
||||||
|
"x.d2": "meow: var replaced",
|
||||||
|
})
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 0, 0, "var replaced", "q")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vars/2",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compileFS(t, "index.d2", map[string]string{
|
||||||
|
"index.d2": "vars: { x: 1 }; ...@a",
|
||||||
|
"a.d2": "vars: { x: 2 }; hi: ${x}",
|
||||||
|
})
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 0, 0, 2, "hi")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vars/3",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compileFS(t, "index.d2", map[string]string{
|
||||||
|
"index.d2": "...@a; vars: { x: 1 }; hi: ${x}",
|
||||||
|
"a.d2": "vars: { x: 2 }",
|
||||||
|
})
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 0, 0, 1, "hi")
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
runa(t, tca)
|
runa(t, tca)
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ func OverlayMap(base, overlay *Map) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, oe := range overlay.Edges {
|
for _, oe := range overlay.Edges {
|
||||||
bea := base.GetEdges(oe.ID)
|
bea := base.GetEdges(oe.ID, nil)
|
||||||
if len(bea) == 0 {
|
if len(bea) == 0 {
|
||||||
base.Edges = append(base.Edges, oe.Copy(base).(*Edge))
|
base.Edges = append(base.Edges, oe.Copy(base).(*Edge))
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
59
d2ir/pattern.go
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
package d2ir
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m *Map) doubleGlob(pattern []string) ([]*Field, bool) {
|
||||||
|
if !(len(pattern) == 3 && pattern[0] == "*" && pattern[1] == "" && pattern[2] == "*") {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
var fa []*Field
|
||||||
|
m._doubleGlob(&fa)
|
||||||
|
return fa, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map) _doubleGlob(fa *[]*Field) {
|
||||||
|
for _, f := range m.Fields {
|
||||||
|
if _, ok := d2graph.ReservedKeywords[f.Name]; ok {
|
||||||
|
if _, ok := d2graph.BoardKeywords[f.Name]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*fa = append(*fa, f)
|
||||||
|
if f.Map() != nil {
|
||||||
|
f.Map()._doubleGlob(fa)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchPattern(s string, pattern []string) bool {
|
||||||
|
if len(pattern) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if _, ok := d2graph.ReservedKeywords[s]; ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(pattern); i++ {
|
||||||
|
if pattern[i] == "*" {
|
||||||
|
// * so match next.
|
||||||
|
if i != len(pattern)-1 {
|
||||||
|
j := strings.Index(strings.ToLower(s), strings.ToLower(pattern[i+1]))
|
||||||
|
if j == -1 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s = s[j+len(pattern[i+1]):]
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !strings.HasPrefix(strings.ToLower(s), strings.ToLower(pattern[i])) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
s = s[len(pattern[i]):]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
334
d2ir/pattern_test.go
Normal file
|
|
@ -0,0 +1,334 @@
|
||||||
|
package d2ir_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/util-go/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testCompilePatterns(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
tca := []testCase{
|
||||||
|
{
|
||||||
|
name: "escaped",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `animal: meow
|
||||||
|
action: yes
|
||||||
|
a\*: globbed`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 3, 0, nil, "")
|
||||||
|
assertQuery(t, m, 0, 0, "meow", "animal")
|
||||||
|
assertQuery(t, m, 0, 0, "yes", "action")
|
||||||
|
assertQuery(t, m, 0, 0, "globbed", `a\*`)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prefix",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `animal: meow
|
||||||
|
action: yes
|
||||||
|
a*: globbed`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 2, 0, nil, "")
|
||||||
|
assertQuery(t, m, 0, 0, "globbed", "animal")
|
||||||
|
assertQuery(t, m, 0, 0, "globbed", "action")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case/1",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `animal: meow
|
||||||
|
action: yes
|
||||||
|
A*: globbed`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 2, 0, nil, "")
|
||||||
|
assertQuery(t, m, 0, 0, "globbed", "animal")
|
||||||
|
assertQuery(t, m, 0, 0, "globbed", "action")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "case/2",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `diddy kong
|
||||||
|
Donkey Kong
|
||||||
|
*kong: yes`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 2, 0, nil, "")
|
||||||
|
assertQuery(t, m, 0, 0, "yes", "diddy kong")
|
||||||
|
assertQuery(t, m, 0, 0, "yes", "Donkey Kong")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "suffix",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `animal: meow
|
||||||
|
jingle: loud
|
||||||
|
*l: globbed`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 2, 0, nil, "")
|
||||||
|
assertQuery(t, m, 0, 0, "globbed", "animal")
|
||||||
|
assertQuery(t, m, 0, 0, "globbed", "jingle")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prefix-suffix",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `tinker: meow
|
||||||
|
thinker: yes
|
||||||
|
t*r: globbed`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 2, 0, nil, "")
|
||||||
|
assertQuery(t, m, 0, 0, "globbed", "tinker")
|
||||||
|
assertQuery(t, m, 0, 0, "globbed", "thinker")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prefix-suffix/2",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `tinker: meow
|
||||||
|
thinker: yes
|
||||||
|
t*ink*r: globbed`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 2, 0, nil, "")
|
||||||
|
assertQuery(t, m, 0, 0, "globbed", "tinker")
|
||||||
|
assertQuery(t, m, 0, 0, "globbed", "thinker")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prefix-suffix/3",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `tinkertinker: meow
|
||||||
|
thinkerthinker: yes
|
||||||
|
t*ink*r*t*inke*: globbed`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 2, 0, nil, "")
|
||||||
|
assertQuery(t, m, 0, 0, "globbed", "tinkertinker")
|
||||||
|
assertQuery(t, m, 0, 0, "globbed", "thinkerthinker")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "nested/prefix-suffix/3",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `animate.constant.tinkertinker: meow
|
||||||
|
astronaut.constant.thinkerthinker: yes
|
||||||
|
a*n*t*.constant.t*ink*r*t*inke*: globbed`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 6, 0, nil, "")
|
||||||
|
assertQuery(t, m, 0, 0, "globbed", "animate.constant.tinkertinker")
|
||||||
|
assertQuery(t, m, 0, 0, "globbed", "astronaut.constant.thinkerthinker")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "edge/1",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `animate
|
||||||
|
animal
|
||||||
|
an* -> an*`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 2, 2, nil, "")
|
||||||
|
assertQuery(t, m, 0, 0, nil, "(animate -> animal)[0]")
|
||||||
|
assertQuery(t, m, 0, 0, nil, "(animal -> animal)[0]")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "edge/2",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `shared.animate
|
||||||
|
shared.animal
|
||||||
|
sh*.(an* -> an*)`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 3, 2, nil, "")
|
||||||
|
assertQuery(t, m, 2, 2, nil, "shared")
|
||||||
|
assertQuery(t, m, 0, 0, nil, "shared.(animate -> animal)[0]")
|
||||||
|
assertQuery(t, m, 0, 0, nil, "shared.(animal -> animate)[0]")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "edge/3",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `shared.animate
|
||||||
|
shared.animal
|
||||||
|
sh*.an* -> sh*.an*`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 3, 2, nil, "")
|
||||||
|
assertQuery(t, m, 2, 2, nil, "shared")
|
||||||
|
assertQuery(t, m, 0, 0, nil, "shared.(animate -> animal)[0]")
|
||||||
|
assertQuery(t, m, 0, 0, nil, "shared.(animal -> animal)[0]")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "edge-glob-index",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `a -> b
|
||||||
|
a -> b
|
||||||
|
a -> b
|
||||||
|
(a -> b)[*].style.fill: red
|
||||||
|
`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 8, 3, nil, "")
|
||||||
|
assertQuery(t, m, 0, 0, "red", "(a -> b)[0].style.fill")
|
||||||
|
assertQuery(t, m, 0, 0, "red", "(a -> b)[1].style.fill")
|
||||||
|
assertQuery(t, m, 0, 0, "red", "(a -> b)[2].style.fill")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "glob-edge-glob-index",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `a -> b
|
||||||
|
a -> b
|
||||||
|
a -> b
|
||||||
|
c -> b
|
||||||
|
(* -> b)[*].style.fill: red
|
||||||
|
`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 11, 4, nil, "")
|
||||||
|
assertQuery(t, m, 0, 0, "red", "(a -> b)[0].style.fill")
|
||||||
|
assertQuery(t, m, 0, 0, "red", "(a -> b)[1].style.fill")
|
||||||
|
assertQuery(t, m, 0, 0, "red", "(a -> b)[2].style.fill")
|
||||||
|
assertQuery(t, m, 0, 0, "red", "(c -> b)[0].style.fill")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "edge-nexus",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `a
|
||||||
|
b
|
||||||
|
c
|
||||||
|
d
|
||||||
|
* -> nexus
|
||||||
|
`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 5, 4, nil, "")
|
||||||
|
assertQuery(t, m, 0, 0, nil, "(a -> nexus)[0]")
|
||||||
|
assertQuery(t, m, 0, 0, nil, "(b -> nexus)[0]")
|
||||||
|
assertQuery(t, m, 0, 0, nil, "(c -> nexus)[0]")
|
||||||
|
assertQuery(t, m, 0, 0, nil, "(d -> nexus)[0]")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "double-glob/1",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `shared.animate
|
||||||
|
shared.animal
|
||||||
|
**.style.fill: red`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 9, 0, nil, "")
|
||||||
|
assertQuery(t, m, 8, 0, nil, "shared")
|
||||||
|
assertQuery(t, m, 1, 0, nil, "shared.style")
|
||||||
|
assertQuery(t, m, 2, 0, nil, "shared.animate")
|
||||||
|
assertQuery(t, m, 1, 0, nil, "shared.animate.style")
|
||||||
|
assertQuery(t, m, 2, 0, nil, "shared.animal")
|
||||||
|
assertQuery(t, m, 1, 0, nil, "shared.animal.style")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "double-glob/edge-no-container",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `zone A: {
|
||||||
|
machine A
|
||||||
|
machine B: {
|
||||||
|
submachine A
|
||||||
|
submachine B
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zone A.** -> load balancer
|
||||||
|
`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 6, 3, nil, "")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "reserved",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `vars: {
|
||||||
|
d2-config: {
|
||||||
|
layout-engine: elk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Spiderman 1
|
||||||
|
Spiderman 2
|
||||||
|
Spiderman 3
|
||||||
|
|
||||||
|
* -> *: arrow`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 6, 6, nil, "")
|
||||||
|
assertQuery(t, m, 0, 0, "arrow", "(* -> *)[*]")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "scenarios",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `
|
||||||
|
|
||||||
|
scenarios: {
|
||||||
|
meow: {
|
||||||
|
e
|
||||||
|
f
|
||||||
|
g
|
||||||
|
h
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a
|
||||||
|
b
|
||||||
|
c
|
||||||
|
d
|
||||||
|
|
||||||
|
**: something
|
||||||
|
** -> **
|
||||||
|
`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 10, 24, nil, "")
|
||||||
|
assertQuery(t, m, 0, 0, "something", "**")
|
||||||
|
assertQuery(t, m, 0, 0, nil, "(* -> *)[*]")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "double-glob/edge/1",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `fast: {
|
||||||
|
a
|
||||||
|
far
|
||||||
|
}
|
||||||
|
|
||||||
|
task: {
|
||||||
|
a
|
||||||
|
}
|
||||||
|
|
||||||
|
task.** -> fast
|
||||||
|
`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 5, 1, nil, "")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "double-glob/edge/2",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
m, err := compile(t, `a
|
||||||
|
|
||||||
|
**.b -> c
|
||||||
|
`)
|
||||||
|
assert.Success(t, err)
|
||||||
|
assertQuery(t, m, 3, 1, nil, "")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
runa(t, tca)
|
||||||
|
|
||||||
|
t.Run("errors", func(t *testing.T) {
|
||||||
|
tca := []testCase{
|
||||||
|
{
|
||||||
|
name: "glob-edge-glob-index",
|
||||||
|
run: func(t testing.TB) {
|
||||||
|
_, err := compile(t, `(* -> b)[*].style.fill: red
|
||||||
|
`)
|
||||||
|
assert.ErrorString(t, err, `TestCompile/patterns/errors/glob-edge-glob-index.d2:1:2: indexed edge does not exist`)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
runa(t, tca)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -29,8 +29,14 @@ func (m *Map) QueryAll(idStr string) (na []Node, _ error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
eida := NewEdgeIDs(k)
|
eida := NewEdgeIDs(k)
|
||||||
for _, eid := range eida {
|
|
||||||
ea := m.GetEdges(eid)
|
for i, eid := range eida {
|
||||||
|
refctx := &RefContext{
|
||||||
|
Key: k,
|
||||||
|
ScopeMap: m,
|
||||||
|
Edge: k.Edges[i],
|
||||||
|
}
|
||||||
|
ea := m.GetEdges(eid, refctx)
|
||||||
for _, e := range ea {
|
for _, e := range ea {
|
||||||
if k.EdgeKey == nil {
|
if k.EdgeKey == nil {
|
||||||
na = append(na, e)
|
na = append(na, e)
|
||||||
|
|
@ -56,7 +62,7 @@ func (m *Map) Query(idStr string) (Node, error) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if len(na) > 1 {
|
if len(na) > 1 {
|
||||||
return nil, fmt.Errorf("expected only one query result but got: %#v", err)
|
return nil, fmt.Errorf("expected only one query result but got: %#v", na)
|
||||||
}
|
}
|
||||||
return na[0], nil
|
return na[0], nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/dop251/goja"
|
"github.com/dop251/goja"
|
||||||
|
|
@ -180,6 +182,11 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
||||||
elkGraph.LayoutOptions.Direction = "DOWN"
|
elkGraph.LayoutOptions.Direction = "DOWN"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set label and icon positions for ELK
|
||||||
|
for _, obj := range g.Objects {
|
||||||
|
positionLabelsIcons(obj)
|
||||||
|
}
|
||||||
|
|
||||||
elkNodes := make(map[*d2graph.Object]*ELKNode)
|
elkNodes := make(map[*d2graph.Object]*ELKNode)
|
||||||
elkEdges := make(map[*d2graph.Edge]*ELKEdge)
|
elkEdges := make(map[*d2graph.Edge]*ELKEdge)
|
||||||
|
|
||||||
|
|
@ -214,18 +221,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
height := obj.Height
|
width, height := adjustDimensions(obj)
|
||||||
width := obj.Width
|
|
||||||
if obj.HasLabel() {
|
|
||||||
if obj.HasOutsideBottomLabel() || obj.Icon != nil {
|
|
||||||
height += float64(obj.LabelDimensions.Height) + label.PADDING
|
|
||||||
}
|
|
||||||
width = go2.Max(width, float64(obj.LabelDimensions.Width))
|
|
||||||
}
|
|
||||||
// reserve extra space for 3d/multiple by providing elk the larger dimensions
|
|
||||||
dx, dy := obj.GetModifierElementAdjustments()
|
|
||||||
width += dx
|
|
||||||
height += dy
|
|
||||||
|
|
||||||
n := &ELKNode{
|
n := &ELKNode{
|
||||||
ID: obj.AbsID(),
|
ID: obj.AbsID(),
|
||||||
|
|
@ -262,41 +258,18 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
||||||
case "RIGHT", "LEFT":
|
case "RIGHT", "LEFT":
|
||||||
n.LayoutOptions.NodeSizeMinimum = fmt.Sprintf("(%d, %d)", int(math.Ceil(width)), int(math.Ceil(height)))
|
n.LayoutOptions.NodeSizeMinimum = fmt.Sprintf("(%d, %d)", int(math.Ceil(width)), int(math.Ceil(height)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.LayoutOptions.Padding == DefaultOpts.Padding {
|
|
||||||
labelHeight := 0
|
|
||||||
if obj.HasLabel() {
|
|
||||||
labelHeight = obj.LabelDimensions.Height + label.PADDING
|
|
||||||
}
|
|
||||||
|
|
||||||
n.Height += 100 + float64(labelHeight)
|
|
||||||
n.Width += 100
|
|
||||||
contentBox := geo.NewBox(geo.NewPoint(0, 0), float64(n.Width), float64(n.Height))
|
|
||||||
shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[obj.Shape.Value]
|
|
||||||
s := shape.NewShape(shapeType, contentBox)
|
|
||||||
|
|
||||||
paddingTop := n.Height - s.GetInnerBox().Height
|
|
||||||
n.Height -= (100 + float64(labelHeight))
|
|
||||||
n.Width -= 100
|
|
||||||
|
|
||||||
iconHeight := 0
|
|
||||||
if obj.Icon != nil && obj.Shape.Value != d2target.ShapeImage {
|
|
||||||
iconHeight = d2target.GetIconSize(s.GetInnerBox(), string(label.InsideTopLeft)) + label.PADDING*2
|
|
||||||
}
|
|
||||||
|
|
||||||
paddingTop += float64(go2.Max(labelHeight, iconHeight))
|
|
||||||
|
|
||||||
n.LayoutOptions.Padding = fmt.Sprintf("[top=%d,left=50,bottom=50,right=50]",
|
|
||||||
// Default padding
|
|
||||||
go2.Max(int(math.Ceil(paddingTop)), 50),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
n.LayoutOptions = &elkOpts{
|
n.LayoutOptions = &elkOpts{
|
||||||
SelfLoopDistribution: "EQUALLY",
|
SelfLoopDistribution: "EQUALLY",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if obj.IsContainer() {
|
||||||
|
padding := parsePadding(opts.Padding)
|
||||||
|
padding = adjustPadding(obj, width, height, padding)
|
||||||
|
n.LayoutOptions.Padding = padding.String()
|
||||||
|
}
|
||||||
|
|
||||||
if obj.HasLabel() {
|
if obj.HasLabel() {
|
||||||
n.Labels = append(n.Labels, &ELKLabel{
|
n.Labels = append(n.Labels, &ELKLabel{
|
||||||
Text: obj.Label.Value,
|
Text: obj.Label.Value,
|
||||||
|
|
@ -313,6 +286,41 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
||||||
elkNodes[obj] = n
|
elkNodes[obj] = n
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// adjust parent padding for children with outside positioned icons
|
||||||
|
for _, obj := range g.Objects {
|
||||||
|
if !obj.IsContainer() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasTop, hasBottom bool
|
||||||
|
for _, child := range obj.ChildrenArray {
|
||||||
|
if child.Shape.Value == d2target.ShapeImage || child.IconPosition == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
switch label.Position(*child.IconPosition) {
|
||||||
|
case label.OutsideTopLeft, label.OutsideTopCenter, label.OutsideTopRight:
|
||||||
|
hasTop = true
|
||||||
|
case label.OutsideBottomLeft, label.OutsideBottomCenter, label.OutsideBottomRight:
|
||||||
|
hasBottom = true
|
||||||
|
}
|
||||||
|
if hasTop && hasBottom {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasTop || hasBottom {
|
||||||
|
padding := parsePadding(elkNodes[obj].LayoutOptions.Padding)
|
||||||
|
if hasTop {
|
||||||
|
padding.top = go2.Max(padding.top, d2target.MAX_ICON_SIZE+2*label.PADDING)
|
||||||
|
}
|
||||||
|
if hasBottom {
|
||||||
|
padding.bottom = go2.Max(padding.bottom, d2target.MAX_ICON_SIZE+2*label.PADDING)
|
||||||
|
}
|
||||||
|
elkNodes[obj].LayoutOptions.Padding = padding.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for _, edge := range g.Edges {
|
for _, edge := range g.Edges {
|
||||||
e := &ELKEdge{
|
e := &ELKEdge{
|
||||||
ID: edge.AbsID(),
|
ID: edge.AbsID(),
|
||||||
|
|
@ -407,29 +415,6 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
||||||
obj.Width = math.Ceil(n.Width)
|
obj.Width = math.Ceil(n.Width)
|
||||||
obj.Height = math.Ceil(n.Height)
|
obj.Height = math.Ceil(n.Height)
|
||||||
|
|
||||||
if obj.Icon != nil && obj.IconPosition == nil {
|
|
||||||
if len(obj.ChildrenArray) > 0 {
|
|
||||||
obj.IconPosition = go2.Pointer(string(label.InsideTopLeft))
|
|
||||||
if obj.LabelPosition == nil {
|
|
||||||
obj.LabelPosition = go2.Pointer(string(label.InsideTopRight))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
obj.IconPosition = go2.Pointer(string(label.InsideMiddleCenter))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if obj.HasLabel() && obj.LabelPosition == nil {
|
|
||||||
if len(obj.ChildrenArray) > 0 {
|
|
||||||
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
|
||||||
} else if obj.HasOutsideBottomLabel() {
|
|
||||||
obj.LabelPosition = go2.Pointer(string(label.OutsideBottomCenter))
|
|
||||||
obj.Height -= float64(obj.LabelDimensions.Height) + label.PADDING
|
|
||||||
} else if obj.Icon != nil {
|
|
||||||
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
|
||||||
} else {
|
|
||||||
obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
byID[obj.AbsID()] = obj
|
byID[obj.AbsID()] = obj
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -463,18 +448,8 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
|
||||||
edge.Route = points
|
edge.Route = points
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the extra width/height we added for 3d/multiple after all objects/connections are placed
|
|
||||||
// and shift the shapes down accordingly
|
|
||||||
for _, obj := range g.Objects {
|
for _, obj := range g.Objects {
|
||||||
dx, dy := obj.GetModifierElementAdjustments()
|
cleanupAdjustment(obj)
|
||||||
if dx != 0 || dy != 0 {
|
|
||||||
obj.TopLeft.Y += dy
|
|
||||||
obj.ShiftDescendants(0, dy)
|
|
||||||
if !obj.IsContainer() {
|
|
||||||
obj.Width -= dx
|
|
||||||
obj.Height -= dy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, edge := range g.Edges {
|
for _, edge := range g.Edges {
|
||||||
|
|
@ -808,3 +783,286 @@ func childrenMaxSelfLoop(parent *d2graph.Object, isWidth bool) int {
|
||||||
|
|
||||||
return max
|
return max
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type shapePadding struct {
|
||||||
|
top, left, bottom, right int
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse out values from elk padding string. e.g. "[top=50,left=50,bottom=50,right=50]"
|
||||||
|
func parsePadding(in string) shapePadding {
|
||||||
|
reTop := regexp.MustCompile(`top=(\d+)`)
|
||||||
|
reLeft := regexp.MustCompile(`left=(\d+)`)
|
||||||
|
reBottom := regexp.MustCompile(`bottom=(\d+)`)
|
||||||
|
reRight := regexp.MustCompile(`right=(\d+)`)
|
||||||
|
|
||||||
|
padding := shapePadding{}
|
||||||
|
|
||||||
|
submatches := reTop.FindStringSubmatch(in)
|
||||||
|
if len(submatches) == 2 {
|
||||||
|
i, err := strconv.ParseInt(submatches[1], 10, 64)
|
||||||
|
if err == nil {
|
||||||
|
padding.top = int(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submatches = reLeft.FindStringSubmatch(in)
|
||||||
|
if len(submatches) == 2 {
|
||||||
|
i, err := strconv.ParseInt(submatches[1], 10, 64)
|
||||||
|
if err == nil {
|
||||||
|
padding.left = int(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submatches = reBottom.FindStringSubmatch(in)
|
||||||
|
if len(submatches) == 2 {
|
||||||
|
i, err := strconv.ParseInt(submatches[1], 10, 64)
|
||||||
|
if err == nil {
|
||||||
|
padding.bottom = int(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submatches = reRight.FindStringSubmatch(in)
|
||||||
|
i, err := strconv.ParseInt(submatches[1], 10, 64)
|
||||||
|
if len(submatches) == 2 {
|
||||||
|
if err == nil {
|
||||||
|
padding.right = int(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return padding
|
||||||
|
}
|
||||||
|
|
||||||
|
func (padding shapePadding) String() string {
|
||||||
|
return fmt.Sprintf("[top=%d,left=%d,bottom=%d,right=%d]", padding.top, padding.left, padding.bottom, padding.right)
|
||||||
|
}
|
||||||
|
|
||||||
|
func adjustPadding(obj *d2graph.Object, width, height float64, padding shapePadding) shapePadding {
|
||||||
|
if !obj.IsContainer() {
|
||||||
|
return padding
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute extra space padding for label/icon
|
||||||
|
var extraTop, extraBottom, extraLeft, extraRight int
|
||||||
|
if obj.HasLabel() && obj.LabelPosition != nil {
|
||||||
|
labelHeight := obj.LabelDimensions.Height + 2*label.PADDING
|
||||||
|
labelWidth := obj.LabelDimensions.Width + 2*label.PADDING
|
||||||
|
switch label.Position(*obj.LabelPosition) {
|
||||||
|
case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight:
|
||||||
|
// Note: for corners we only add height
|
||||||
|
extraTop = labelHeight
|
||||||
|
case label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight:
|
||||||
|
extraBottom = labelHeight
|
||||||
|
case label.InsideMiddleLeft:
|
||||||
|
extraLeft = labelWidth
|
||||||
|
case label.InsideMiddleRight:
|
||||||
|
extraRight = labelWidth
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if obj.Icon != nil && obj.Shape.Value != d2target.ShapeImage && obj.IconPosition != nil {
|
||||||
|
iconSize := d2target.MAX_ICON_SIZE + 2*label.PADDING
|
||||||
|
switch label.Position(*obj.IconPosition) {
|
||||||
|
case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight:
|
||||||
|
extraTop = go2.Max(extraTop, iconSize)
|
||||||
|
case label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight:
|
||||||
|
extraBottom = go2.Max(extraBottom, iconSize)
|
||||||
|
case label.InsideMiddleLeft:
|
||||||
|
extraLeft = go2.Max(extraLeft, iconSize)
|
||||||
|
case label.InsideMiddleRight:
|
||||||
|
extraRight = go2.Max(extraRight, iconSize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxChildWidth, maxChildHeight := math.Inf(-1), math.Inf(-1)
|
||||||
|
for _, c := range obj.ChildrenArray {
|
||||||
|
if c.Width > maxChildWidth {
|
||||||
|
maxChildWidth = c.Width
|
||||||
|
}
|
||||||
|
if c.Height > maxChildHeight {
|
||||||
|
maxChildHeight = c.Height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// We don't know exactly what the shape dimensions will be after layout, but for more accurate innerBox dimensions,
|
||||||
|
// we add the maxChildWidth and maxChildHeight with computed additions for the innerBox calculation
|
||||||
|
width += maxChildWidth + float64(extraLeft+extraRight)
|
||||||
|
height += maxChildHeight + float64(extraTop+extraBottom)
|
||||||
|
contentBox := geo.NewBox(geo.NewPoint(0, 0), width, height)
|
||||||
|
shapeType := d2target.DSL_SHAPE_TO_SHAPE_TYPE[obj.Shape.Value]
|
||||||
|
s := shape.NewShape(shapeType, contentBox)
|
||||||
|
innerBox := s.GetInnerBox()
|
||||||
|
|
||||||
|
// If the shape inner box + label/icon height becomes greater than the default padding, we want to use that
|
||||||
|
//
|
||||||
|
// ┌OUTER───────────────────────────┬────────────────────────────────────────────┐
|
||||||
|
// │ │ │
|
||||||
|
// │ ┌INNER──────── ┬ ─────────────│───────────────────────────────────────┐ │
|
||||||
|
// │ │ │Label Padding │ │ │
|
||||||
|
// │ │ ┌LABEL─ ┴ ─────────────│───────┐┬ ┌ICON── ┬ ────┐ │ │
|
||||||
|
// │ │ │ │ ││ │ │ │ │ │
|
||||||
|
// │ │ │ │ ││Label Height │ Icon│ │ │ │
|
||||||
|
// │ │ │ │ ││ │ Height│ │ │ │
|
||||||
|
// │ │ └──────────────────────│───────┘┴ │ │ │ │ │
|
||||||
|
// │ │ │ └────── ┴ ────┘ │ │
|
||||||
|
// │ │ │ │ │
|
||||||
|
// │ │ ┴Default ELK Padding │ │
|
||||||
|
// │ │ ┌CHILD────────────────────────────────────────────────────────┐ │ │
|
||||||
|
// │ │ │ │ │ │
|
||||||
|
// │ │ │ │ │ │
|
||||||
|
// │ │ │ │ │ │
|
||||||
|
// │ │ └─────────────────────────────────────────────────────────────┘ │ │
|
||||||
|
// │ │ │ │
|
||||||
|
// │ └─────────────────────────────────────────────────────────────────────┘ │
|
||||||
|
// │ │
|
||||||
|
// └─────────────────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
|
// estimated shape innerBox padding
|
||||||
|
innerTop := int(math.Ceil(innerBox.TopLeft.Y))
|
||||||
|
innerBottom := int(math.Ceil(height - (innerBox.TopLeft.Y + innerBox.Height)))
|
||||||
|
innerLeft := int(math.Ceil(innerBox.TopLeft.X))
|
||||||
|
innerRight := int(math.Ceil(width - (innerBox.TopLeft.X + innerBox.Width)))
|
||||||
|
|
||||||
|
padding.top = go2.Max(padding.top, innerTop+extraTop)
|
||||||
|
padding.bottom = go2.Max(padding.bottom, innerBottom+extraBottom)
|
||||||
|
padding.left = go2.Max(padding.left, innerLeft+extraLeft)
|
||||||
|
padding.right = go2.Max(padding.right, innerRight+extraRight)
|
||||||
|
|
||||||
|
return padding
|
||||||
|
}
|
||||||
|
|
||||||
|
func adjustDimensions(obj *d2graph.Object) (width, height float64) {
|
||||||
|
width = obj.Width
|
||||||
|
height = obj.Height
|
||||||
|
|
||||||
|
// reserve spacing for labels
|
||||||
|
if obj.HasLabel() {
|
||||||
|
var position label.Position
|
||||||
|
if obj.LabelPosition != nil {
|
||||||
|
position = label.Position(*obj.LabelPosition)
|
||||||
|
} else if len(obj.ChildrenArray) == 0 && obj.HasOutsideBottomLabel() {
|
||||||
|
position = label.OutsideBottomCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
if position.IsShapePosition() {
|
||||||
|
switch position {
|
||||||
|
case label.OutsideLeftTop, label.OutsideLeftMiddle, label.OutsideLeftBottom,
|
||||||
|
label.OutsideRightTop, label.OutsideRightMiddle, label.OutsideRightBottom:
|
||||||
|
width += float64(obj.LabelDimensions.Width) + label.PADDING
|
||||||
|
default:
|
||||||
|
// TODO labelWidth+2*label.PADDING
|
||||||
|
width = go2.Max(width, float64(obj.LabelDimensions.Width))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// special handling
|
||||||
|
if obj.HasOutsideBottomLabel() || obj.Icon != nil {
|
||||||
|
height += float64(obj.LabelDimensions.Height) + label.PADDING
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj.Icon != nil && obj.Shape.Value != d2target.ShapeImage {
|
||||||
|
var position label.Position
|
||||||
|
if obj.IconPosition != nil {
|
||||||
|
position = label.Position(*obj.IconPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
if position.IsShapePosition() {
|
||||||
|
switch position {
|
||||||
|
case label.OutsideLeftTop, label.OutsideLeftMiddle, label.OutsideLeftBottom,
|
||||||
|
label.OutsideRightTop, label.OutsideRightMiddle, label.OutsideRightBottom:
|
||||||
|
width += d2target.MAX_ICON_SIZE + label.PADDING
|
||||||
|
default:
|
||||||
|
width = go2.Max(width, d2target.MAX_ICON_SIZE+2*label.PADDING)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reserve extra space for 3d/multiple by providing elk the larger dimensions
|
||||||
|
dx, dy := obj.GetModifierElementAdjustments()
|
||||||
|
width += dx
|
||||||
|
height += dy
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func cleanupAdjustment(obj *d2graph.Object) {
|
||||||
|
// adjust size and position to account for space reserved for labels
|
||||||
|
if obj.HasLabel() {
|
||||||
|
position := label.Position(*obj.LabelPosition)
|
||||||
|
if position.IsShapePosition() {
|
||||||
|
var labelWidth float64
|
||||||
|
switch position {
|
||||||
|
case label.OutsideLeftTop, label.OutsideLeftMiddle, label.OutsideLeftBottom,
|
||||||
|
label.OutsideRightTop, label.OutsideRightMiddle, label.OutsideRightBottom:
|
||||||
|
labelWidth = float64(obj.LabelDimensions.Width) + label.PADDING
|
||||||
|
obj.Width -= labelWidth
|
||||||
|
}
|
||||||
|
switch position {
|
||||||
|
case label.OutsideLeftTop, label.OutsideLeftMiddle, label.OutsideLeftBottom:
|
||||||
|
obj.TopLeft.X += labelWidth
|
||||||
|
obj.ShiftDescendants(labelWidth/2, 0)
|
||||||
|
case label.OutsideRightTop, label.OutsideRightMiddle, label.OutsideRightBottom:
|
||||||
|
obj.ShiftDescendants(-labelWidth/2, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if obj.Icon != nil && obj.Shape.Value != d2target.ShapeImage {
|
||||||
|
position := label.Position(*obj.IconPosition)
|
||||||
|
if position.IsShapePosition() {
|
||||||
|
var iconWidth float64
|
||||||
|
switch position {
|
||||||
|
case label.OutsideLeftTop, label.OutsideLeftMiddle, label.OutsideLeftBottom,
|
||||||
|
label.OutsideRightTop, label.OutsideRightMiddle, label.OutsideRightBottom:
|
||||||
|
iconWidth = d2target.MAX_ICON_SIZE + label.PADDING
|
||||||
|
obj.Width -= iconWidth
|
||||||
|
}
|
||||||
|
switch position {
|
||||||
|
case label.OutsideLeftTop, label.OutsideLeftMiddle, label.OutsideLeftBottom:
|
||||||
|
obj.TopLeft.X += iconWidth
|
||||||
|
obj.ShiftDescendants(iconWidth/2, 0)
|
||||||
|
case label.OutsideRightTop, label.OutsideRightMiddle, label.OutsideRightBottom:
|
||||||
|
obj.ShiftDescendants(-iconWidth/2, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// special handling to start/end connections below label
|
||||||
|
if obj.HasOutsideBottomLabel() {
|
||||||
|
obj.Height -= float64(obj.LabelDimensions.Height) + label.PADDING
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the extra width/height we added for 3d/multiple after all objects/connections are placed
|
||||||
|
// and shift the shapes down accordingly
|
||||||
|
dx, dy := obj.GetModifierElementAdjustments()
|
||||||
|
if dx != 0 || dy != 0 {
|
||||||
|
obj.TopLeft.Y += dy
|
||||||
|
obj.ShiftDescendants(0, dy)
|
||||||
|
if !obj.IsContainer() {
|
||||||
|
obj.Width -= dx
|
||||||
|
obj.Height -= dy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func positionLabelsIcons(obj *d2graph.Object) {
|
||||||
|
if obj.Icon != nil && obj.IconPosition == nil {
|
||||||
|
if len(obj.ChildrenArray) > 0 {
|
||||||
|
obj.IconPosition = go2.Pointer(string(label.InsideTopLeft))
|
||||||
|
if obj.LabelPosition == nil {
|
||||||
|
obj.LabelPosition = go2.Pointer(string(label.InsideTopRight))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
obj.IconPosition = go2.Pointer(string(label.InsideMiddleCenter))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if obj.HasLabel() && obj.LabelPosition == nil {
|
||||||
|
if len(obj.ChildrenArray) > 0 {
|
||||||
|
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||||
|
} else if obj.HasOutsideBottomLabel() {
|
||||||
|
obj.LabelPosition = go2.Pointer(string(label.OutsideBottomCenter))
|
||||||
|
} else if obj.Icon != nil {
|
||||||
|
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
|
||||||
|
} else {
|
||||||
|
obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ n2 -> n1: right to left
|
||||||
n1 -> n2
|
n1 -> n2
|
||||||
n2 -> n1
|
n2 -> n1
|
||||||
`
|
`
|
||||||
g, err := d2compiler.Compile("", strings.NewReader(input), nil)
|
g, _, err := d2compiler.Compile("", strings.NewReader(input), nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
n1, has := g.Root.HasChild([]string{"n1"})
|
n1, has := g.Root.HasChild([]string{"n1"})
|
||||||
|
|
@ -177,7 +177,7 @@ a.t2 -> b
|
||||||
b -> a.t2`
|
b -> a.t2`
|
||||||
|
|
||||||
ctx := log.WithTB(context.Background(), t, nil)
|
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)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
g.Root.Shape = d2graph.Scalar{Value: d2target.ShapeSequenceDiagram}
|
g.Root.Shape = d2graph.Scalar{Value: d2target.ShapeSequenceDiagram}
|
||||||
|
|
@ -298,7 +298,7 @@ c
|
||||||
container -> c: edge 1
|
container -> c: edge 1
|
||||||
`
|
`
|
||||||
ctx := log.WithTB(context.Background(), t, nil)
|
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)
|
assert.Nil(t, err)
|
||||||
|
|
||||||
container, has := g.Root.HasChild([]string{"container"})
|
container, has := g.Root.HasChild([]string{"container"})
|
||||||
|
|
|
||||||
102
d2lib/d2.go
|
|
@ -15,8 +15,11 @@ import (
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2near"
|
"oss.terrastruct.com/d2/d2layouts/d2near"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2sequence"
|
"oss.terrastruct.com/d2/d2layouts/d2sequence"
|
||||||
"oss.terrastruct.com/d2/d2renderers/d2fonts"
|
"oss.terrastruct.com/d2/d2renderers/d2fonts"
|
||||||
|
"oss.terrastruct.com/d2/d2renderers/d2svg"
|
||||||
"oss.terrastruct.com/d2/d2target"
|
"oss.terrastruct.com/d2/d2target"
|
||||||
|
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
|
||||||
"oss.terrastruct.com/d2/lib/textmeasure"
|
"oss.terrastruct.com/d2/lib/textmeasure"
|
||||||
|
"oss.terrastruct.com/util-go/go2"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CompileOptions struct {
|
type CompileOptions struct {
|
||||||
|
|
@ -24,8 +27,9 @@ type CompileOptions struct {
|
||||||
FS fs.FS
|
FS fs.FS
|
||||||
MeasuredTexts []*d2target.MText
|
MeasuredTexts []*d2target.MText
|
||||||
Ruler *textmeasure.Ruler
|
Ruler *textmeasure.Ruler
|
||||||
Layout func(context.Context, *d2graph.Graph) error
|
LayoutResolver func(engine string) (d2graph.LayoutGraph, error)
|
||||||
ThemeID int64
|
|
||||||
|
Layout *string
|
||||||
|
|
||||||
// FontFamily controls the font family used for all texts that are not the following:
|
// FontFamily controls the font family used for all texts that are not the following:
|
||||||
// - code
|
// - code
|
||||||
|
|
@ -37,39 +41,45 @@ type CompileOptions struct {
|
||||||
InputPath string
|
InputPath string
|
||||||
}
|
}
|
||||||
|
|
||||||
func Compile(ctx context.Context, input string, opts *CompileOptions) (*d2target.Diagram, *d2graph.Graph, error) {
|
func Compile(ctx context.Context, input string, compileOpts *CompileOptions, renderOpts *d2svg.RenderOpts) (*d2target.Diagram, *d2graph.Graph, error) {
|
||||||
if opts == nil {
|
if compileOpts == nil {
|
||||||
opts = &CompileOptions{}
|
compileOpts = &CompileOptions{}
|
||||||
|
}
|
||||||
|
if renderOpts == nil {
|
||||||
|
renderOpts = &d2svg.RenderOpts{}
|
||||||
}
|
}
|
||||||
|
|
||||||
g, err := d2compiler.Compile(opts.InputPath, strings.NewReader(input), &d2compiler.CompileOptions{
|
g, config, err := d2compiler.Compile(compileOpts.InputPath, strings.NewReader(input), &d2compiler.CompileOptions{
|
||||||
UTF16: opts.UTF16,
|
UTF16: compileOpts.UTF16,
|
||||||
FS: opts.FS,
|
FS: compileOpts.FS,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
d, err := compile(ctx, g, opts)
|
applyConfigs(config, compileOpts, renderOpts)
|
||||||
if err != nil {
|
applyDefaults(compileOpts, renderOpts)
|
||||||
return nil, nil, err
|
|
||||||
|
d, err := compile(ctx, g, compileOpts, renderOpts)
|
||||||
|
if d != nil {
|
||||||
|
d.Config = config
|
||||||
}
|
}
|
||||||
return d, g, nil
|
return d, g, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func compile(ctx context.Context, g *d2graph.Graph, opts *CompileOptions) (*d2target.Diagram, error) {
|
func compile(ctx context.Context, g *d2graph.Graph, compileOpts *CompileOptions, renderOpts *d2svg.RenderOpts) (*d2target.Diagram, error) {
|
||||||
err := g.ApplyTheme(opts.ThemeID)
|
err := g.ApplyTheme(*renderOpts.ThemeID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(g.Objects) > 0 {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
coreLayout, err := getLayout(opts)
|
coreLayout, err := getLayout(compileOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -96,27 +106,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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, l := range g.Layers {
|
for _, l := range g.Layers {
|
||||||
ld, err := compile(ctx, l, opts)
|
ld, err := compile(ctx, l, compileOpts, renderOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
d.Layers = append(d.Layers, ld)
|
d.Layers = append(d.Layers, ld)
|
||||||
}
|
}
|
||||||
for _, l := range g.Scenarios {
|
for _, l := range g.Scenarios {
|
||||||
ld, err := compile(ctx, l, opts)
|
ld, err := compile(ctx, l, compileOpts, renderOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
d.Scenarios = append(d.Scenarios, ld)
|
d.Scenarios = append(d.Scenarios, ld)
|
||||||
}
|
}
|
||||||
for _, l := range g.Steps {
|
for _, l := range g.Steps {
|
||||||
ld, err := compile(ctx, l, opts)
|
ld, err := compile(ctx, l, compileOpts, renderOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +137,7 @@ func compile(ctx context.Context, g *d2graph.Graph, opts *CompileOptions) (*d2ta
|
||||||
|
|
||||||
func getLayout(opts *CompileOptions) (d2graph.LayoutGraph, error) {
|
func getLayout(opts *CompileOptions) (d2graph.LayoutGraph, error) {
|
||||||
if opts.Layout != nil {
|
if opts.Layout != nil {
|
||||||
return opts.Layout, nil
|
return opts.LayoutResolver(*opts.Layout)
|
||||||
} else if os.Getenv("D2_LAYOUT") == "dagre" {
|
} else if os.Getenv("D2_LAYOUT") == "dagre" {
|
||||||
defaultLayout := func(ctx context.Context, g *d2graph.Graph) error {
|
defaultLayout := func(ctx context.Context, g *d2graph.Graph) error {
|
||||||
return d2dagrelayout.Layout(ctx, g, nil)
|
return d2dagrelayout.Layout(ctx, g, nil)
|
||||||
|
|
@ -137,3 +147,53 @@ func getLayout(opts *CompileOptions) (d2graph.LayoutGraph, error) {
|
||||||
return nil, errors.New("no available layout")
|
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) {
|
func recompile(ast *d2ast.Map) (*d2graph.Graph, error) {
|
||||||
s := d2format.Format(ast)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to recompile:\n%s\n%w", s, err)
|
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) {
|
func (tc editTest) run(t *testing.T) {
|
||||||
d2Path := fmt.Sprintf("d2/testdata/d2oracle/%v.d2", t.Name())
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -7265,7 +7265,7 @@ scenarios: {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
d2Path := fmt.Sprintf("d2/testdata/d2oracle/%v.d2", t.Name())
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -7725,7 +7725,7 @@ z
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
d2Path := fmt.Sprintf("d2/testdata/d2oracle/%v.d2", t.Name())
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -8095,7 +8095,7 @@ layers: {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
d2Path := fmt.Sprintf("d2/testdata/d2oracle/%v.d2", t.Name())
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
@ -8325,7 +8325,7 @@ scenarios: {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
d2Path := fmt.Sprintf("d2/testdata/d2oracle/%v.d2", t.Name())
|
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 {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1030,9 +1030,15 @@ func (p *parser) parseUnquotedString(inKey bool) (s *d2ast.UnquotedString) {
|
||||||
|
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
var rawb strings.Builder
|
var rawb strings.Builder
|
||||||
|
lastPatternIndex := 0
|
||||||
defer func() {
|
defer func() {
|
||||||
sv := strings.TrimRightFunc(sb.String(), unicode.IsSpace)
|
sv := strings.TrimRightFunc(sb.String(), unicode.IsSpace)
|
||||||
rawv := strings.TrimRightFunc(rawb.String(), unicode.IsSpace)
|
rawv := strings.TrimRightFunc(rawb.String(), unicode.IsSpace)
|
||||||
|
if s.Pattern != nil {
|
||||||
|
if lastPatternIndex < len(sv) {
|
||||||
|
s.Pattern = append(s.Pattern, sv[lastPatternIndex:])
|
||||||
|
}
|
||||||
|
}
|
||||||
if sv == "" {
|
if sv == "" {
|
||||||
if len(s.Value) > 0 {
|
if len(s.Value) > 0 {
|
||||||
return
|
return
|
||||||
|
|
@ -1092,7 +1098,7 @@ func (p *parser) parseUnquotedString(inKey bool) (s *d2ast.UnquotedString) {
|
||||||
}
|
}
|
||||||
if inKey {
|
if inKey {
|
||||||
switch r {
|
switch r {
|
||||||
case ':', '.', '<', '>':
|
case ':', '.', '<', '>', '&':
|
||||||
p.rewind()
|
p.rewind()
|
||||||
return s
|
return s
|
||||||
case '-':
|
case '-':
|
||||||
|
|
@ -1118,18 +1124,12 @@ func (p *parser) parseUnquotedString(inKey bool) (s *d2ast.UnquotedString) {
|
||||||
rawb.WriteRune(r)
|
rawb.WriteRune(r)
|
||||||
r = r2
|
r = r2
|
||||||
case '*':
|
case '*':
|
||||||
// TODO: need a peekNotSpace across escaped newlines
|
if sb.Len() == 0 {
|
||||||
r2, eof := p.peek()
|
s.Pattern = append(s.Pattern, "*")
|
||||||
if eof {
|
} else {
|
||||||
return s
|
s.Pattern = append(s.Pattern, sb.String()[lastPatternIndex:], "*")
|
||||||
}
|
}
|
||||||
if r2 == '-' {
|
lastPatternIndex = len(sb.String()) + 1
|
||||||
p.rewind()
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
sb.WriteRune(r)
|
|
||||||
rawb.WriteRune(r)
|
|
||||||
r = r2
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1560,7 +1560,8 @@ func (p *parser) parseArrayNode(r rune) d2ast.ArrayNodeBox {
|
||||||
|
|
||||||
p.replay(r)
|
p.replay(r)
|
||||||
vbox := p.parseValue()
|
vbox := p.parseValue()
|
||||||
if vbox.UnquotedString != nil && vbox.UnquotedString.ScalarString() == "" {
|
if vbox.UnquotedString != nil && vbox.UnquotedString.ScalarString() == "" &&
|
||||||
|
!(len(vbox.UnquotedString.Value) > 0 && vbox.UnquotedString.Value[0].Substitution != nil) {
|
||||||
p.errorf(p.pos, p.pos.Advance(r, p.utf16), "unquoted strings cannot start on %q", r)
|
p.errorf(p.pos, p.pos.Advance(r, p.utf16), "unquoted strings cannot start on %q", r)
|
||||||
}
|
}
|
||||||
box.Null = vbox.Null
|
box.Null = vbox.Null
|
||||||
|
|
|
||||||
|
|
@ -49,10 +49,10 @@ func Wrap(rootDiagram *d2target.Diagram, svgs [][]byte, renderOpts d2svg.RenderO
|
||||||
// TODO account for stroke width of root border
|
// TODO account for stroke width of root border
|
||||||
|
|
||||||
tl, br := rootDiagram.NestedBoundingBox()
|
tl, br := rootDiagram.NestedBoundingBox()
|
||||||
left := tl.X - renderOpts.Pad
|
left := tl.X - int(*renderOpts.Pad)
|
||||||
top := tl.Y - renderOpts.Pad
|
top := tl.Y - int(*renderOpts.Pad)
|
||||||
width := br.X - tl.X + renderOpts.Pad*2
|
width := br.X - tl.X + int(*renderOpts.Pad)*2
|
||||||
height := br.Y - tl.Y + 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">`,
|
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,
|
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)
|
fmt.Fprintf(buf, `<style type="text/css">%s</style>`, css)
|
||||||
}
|
}
|
||||||
|
|
||||||
if renderOpts.Sketch {
|
if renderOpts.Sketch != nil && *renderOpts.Sketch {
|
||||||
d2sketch.DefineFillPatterns(buf)
|
d2sketch.DefineFillPatterns(buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"oss.terrastruct.com/util-go/diff"
|
"oss.terrastruct.com/util-go/diff"
|
||||||
"oss.terrastruct.com/util-go/go2"
|
"oss.terrastruct.com/util-go/go2"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2elklayout"
|
"oss.terrastruct.com/d2/d2layouts/d2elklayout"
|
||||||
"oss.terrastruct.com/d2/d2lib"
|
"oss.terrastruct.com/d2/d2lib"
|
||||||
|
|
@ -1339,16 +1340,22 @@ func run(t *testing.T, tc testCase) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
layout := d2dagrelayout.DefaultLayout
|
layoutResolver := func(engine string) (d2graph.LayoutGraph, error) {
|
||||||
if strings.EqualFold(tc.engine, "elk") {
|
if strings.EqualFold(engine, "elk") {
|
||||||
layout = d2elklayout.DefaultLayout
|
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{
|
diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
|
||||||
Ruler: ruler,
|
Ruler: ruler,
|
||||||
Layout: layout,
|
Layout: &tc.engine,
|
||||||
|
LayoutResolver: layoutResolver,
|
||||||
FontFamily: go2.Pointer(d2fonts.HandDrawn),
|
FontFamily: go2.Pointer(d2fonts.HandDrawn),
|
||||||
ThemeID: tc.themeID,
|
}, renderOpts)
|
||||||
})
|
|
||||||
if !tassert.Nil(t, err) {
|
if !tassert.Nil(t, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -1356,11 +1363,7 @@ func run(t *testing.T, tc testCase) {
|
||||||
dataPath := filepath.Join("testdata", strings.TrimPrefix(t.Name(), "TestSketch/"))
|
dataPath := filepath.Join("testdata", strings.TrimPrefix(t.Name(), "TestSketch/"))
|
||||||
pathGotSVG := filepath.Join(dataPath, "sketch.got.svg")
|
pathGotSVG := filepath.Join(dataPath, "sketch.got.svg")
|
||||||
|
|
||||||
svgBytes, err := d2svg.Render(diagram, &d2svg.RenderOpts{
|
svgBytes, err := d2svg.Render(diagram, renderOpts)
|
||||||
Pad: d2svg.DEFAULT_PADDING,
|
|
||||||
Sketch: true,
|
|
||||||
ThemeID: tc.themeID,
|
|
||||||
})
|
|
||||||
assert.Success(t, err)
|
assert.Success(t, err)
|
||||||
err = os.MkdirAll(dataPath, 0755)
|
err = os.MkdirAll(dataPath, 0755)
|
||||||
assert.Success(t, err)
|
assert.Success(t, err)
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 129 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 121 KiB |
154
d2renderers/d2sketch/testdata/basic/sketch.exp.svg
vendored
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
154
d2renderers/d2sketch/testdata/class/sketch.exp.svg
vendored
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 163 KiB |
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 154 KiB |
168
d2renderers/d2sketch/testdata/dots-3d/sketch.exp.svg
vendored
|
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
|
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 163 KiB |
|
Before Width: | Height: | Size: 167 KiB After Width: | Height: | Size: 167 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 115 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
560
d2renderers/d2sketch/testdata/opacity/sketch.exp.svg
vendored
|
Before Width: | Height: | Size: 110 KiB After Width: | Height: | Size: 110 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 101 KiB |
154
d2renderers/d2sketch/testdata/overlay/sketch.exp.svg
vendored
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 493 KiB After Width: | Height: | Size: 497 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 113 KiB |
644
d2renderers/d2sketch/testdata/twitter/sketch.exp.svg
vendored
|
Before Width: | Height: | Size: 217 KiB After Width: | Height: | Size: 216 KiB |
|
Before Width: | Height: | Size: 217 KiB After Width: | Height: | Size: 216 KiB |
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"oss.terrastruct.com/util-go/assert"
|
"oss.terrastruct.com/util-go/assert"
|
||||||
"oss.terrastruct.com/util-go/diff"
|
"oss.terrastruct.com/util-go/diff"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||||
"oss.terrastruct.com/d2/d2lib"
|
"oss.terrastruct.com/d2/d2lib"
|
||||||
"oss.terrastruct.com/d2/d2renderers/d2svg"
|
"oss.terrastruct.com/d2/d2renderers/d2svg"
|
||||||
|
|
@ -152,11 +153,17 @@ func run(t *testing.T, tc testCase) {
|
||||||
return
|
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{
|
diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
|
||||||
Ruler: ruler,
|
Ruler: ruler,
|
||||||
Layout: d2dagrelayout.DefaultLayout,
|
LayoutResolver: layoutResolver,
|
||||||
ThemeID: tc.themeID,
|
}, renderOpts)
|
||||||
})
|
|
||||||
if !tassert.Nil(t, err) {
|
if !tassert.Nil(t, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -164,10 +171,7 @@ func run(t *testing.T, tc testCase) {
|
||||||
dataPath := filepath.Join("testdata", strings.TrimPrefix(t.Name(), "TestAppendix/"))
|
dataPath := filepath.Join("testdata", strings.TrimPrefix(t.Name(), "TestAppendix/"))
|
||||||
pathGotSVG := filepath.Join(dataPath, "sketch.got.svg")
|
pathGotSVG := filepath.Join(dataPath, "sketch.got.svg")
|
||||||
|
|
||||||
svgBytes, err := d2svg.Render(diagram, &d2svg.RenderOpts{
|
svgBytes, err := d2svg.Render(diagram, renderOpts)
|
||||||
Pad: d2svg.DEFAULT_PADDING,
|
|
||||||
ThemeID: tc.themeID,
|
|
||||||
})
|
|
||||||
assert.Success(t, err)
|
assert.Success(t, err)
|
||||||
svgBytes = appendix.Append(diagram, ruler, svgBytes)
|
svgBytes = appendix.Append(diagram, ruler, svgBytes)
|
||||||
|
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 677 KiB After Width: | Height: | Size: 677 KiB |
|
|
@ -1,12 +1,12 @@
|
||||||
<?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 304 407"><svg id="d2-svg" class="d2-916646398" width="304" height="407" viewBox="-101 -118 304 407"><rect x="-101.000000" y="-118.000000" width="304" height="407" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
<?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.6.0-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 304 407"><svg id="d2-svg" class="d2-3057089836" width="304" height="407" viewBox="-101 -118 304 407"><rect x="-101.000000" y="-118.000000" width="304" height="407" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||||
.appendix-icon {
|
.appendix-icon {
|
||||||
filter: drop-shadow(0px 0px 32px rgba(31, 36, 58, 0.1));
|
filter: drop-shadow(0px 0px 32px rgba(31, 36, 58, 0.1));
|
||||||
}
|
}
|
||||||
.d2-916646398 .text-bold {
|
.d2-3057089836 .text-bold {
|
||||||
font-family: "d2-916646398-font-bold";
|
font-family: "d2-3057089836-font-bold";
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: d2-916646398-font-bold;
|
font-family: d2-3057089836-font-bold;
|
||||||
src: url("data:application/font-woff;base64,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");
|
src: url("data:application/font-woff;base64,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");
|
||||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||||
shape-rendering: geometricPrecision;
|
shape-rendering: geometricPrecision;
|
||||||
|
|
@ -21,78 +21,78 @@
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2-916646398 .fill-N1{fill:#0A0F25;}
|
.d2-3057089836 .fill-N1{fill:#0A0F25;}
|
||||||
.d2-916646398 .fill-N2{fill:#676C7E;}
|
.d2-3057089836 .fill-N2{fill:#676C7E;}
|
||||||
.d2-916646398 .fill-N3{fill:#9499AB;}
|
.d2-3057089836 .fill-N3{fill:#9499AB;}
|
||||||
.d2-916646398 .fill-N4{fill:#CFD2DD;}
|
.d2-3057089836 .fill-N4{fill:#CFD2DD;}
|
||||||
.d2-916646398 .fill-N5{fill:#DEE1EB;}
|
.d2-3057089836 .fill-N5{fill:#DEE1EB;}
|
||||||
.d2-916646398 .fill-N6{fill:#EEF1F8;}
|
.d2-3057089836 .fill-N6{fill:#EEF1F8;}
|
||||||
.d2-916646398 .fill-N7{fill:#FFFFFF;}
|
.d2-3057089836 .fill-N7{fill:#FFFFFF;}
|
||||||
.d2-916646398 .fill-B1{fill:#0D32B2;}
|
.d2-3057089836 .fill-B1{fill:#0D32B2;}
|
||||||
.d2-916646398 .fill-B2{fill:#0D32B2;}
|
.d2-3057089836 .fill-B2{fill:#0D32B2;}
|
||||||
.d2-916646398 .fill-B3{fill:#E3E9FD;}
|
.d2-3057089836 .fill-B3{fill:#E3E9FD;}
|
||||||
.d2-916646398 .fill-B4{fill:#E3E9FD;}
|
.d2-3057089836 .fill-B4{fill:#E3E9FD;}
|
||||||
.d2-916646398 .fill-B5{fill:#EDF0FD;}
|
.d2-3057089836 .fill-B5{fill:#EDF0FD;}
|
||||||
.d2-916646398 .fill-B6{fill:#F7F8FE;}
|
.d2-3057089836 .fill-B6{fill:#F7F8FE;}
|
||||||
.d2-916646398 .fill-AA2{fill:#4A6FF3;}
|
.d2-3057089836 .fill-AA2{fill:#4A6FF3;}
|
||||||
.d2-916646398 .fill-AA4{fill:#EDF0FD;}
|
.d2-3057089836 .fill-AA4{fill:#EDF0FD;}
|
||||||
.d2-916646398 .fill-AA5{fill:#F7F8FE;}
|
.d2-3057089836 .fill-AA5{fill:#F7F8FE;}
|
||||||
.d2-916646398 .fill-AB4{fill:#EDF0FD;}
|
.d2-3057089836 .fill-AB4{fill:#EDF0FD;}
|
||||||
.d2-916646398 .fill-AB5{fill:#F7F8FE;}
|
.d2-3057089836 .fill-AB5{fill:#F7F8FE;}
|
||||||
.d2-916646398 .stroke-N1{stroke:#0A0F25;}
|
.d2-3057089836 .stroke-N1{stroke:#0A0F25;}
|
||||||
.d2-916646398 .stroke-N2{stroke:#676C7E;}
|
.d2-3057089836 .stroke-N2{stroke:#676C7E;}
|
||||||
.d2-916646398 .stroke-N3{stroke:#9499AB;}
|
.d2-3057089836 .stroke-N3{stroke:#9499AB;}
|
||||||
.d2-916646398 .stroke-N4{stroke:#CFD2DD;}
|
.d2-3057089836 .stroke-N4{stroke:#CFD2DD;}
|
||||||
.d2-916646398 .stroke-N5{stroke:#DEE1EB;}
|
.d2-3057089836 .stroke-N5{stroke:#DEE1EB;}
|
||||||
.d2-916646398 .stroke-N6{stroke:#EEF1F8;}
|
.d2-3057089836 .stroke-N6{stroke:#EEF1F8;}
|
||||||
.d2-916646398 .stroke-N7{stroke:#FFFFFF;}
|
.d2-3057089836 .stroke-N7{stroke:#FFFFFF;}
|
||||||
.d2-916646398 .stroke-B1{stroke:#0D32B2;}
|
.d2-3057089836 .stroke-B1{stroke:#0D32B2;}
|
||||||
.d2-916646398 .stroke-B2{stroke:#0D32B2;}
|
.d2-3057089836 .stroke-B2{stroke:#0D32B2;}
|
||||||
.d2-916646398 .stroke-B3{stroke:#E3E9FD;}
|
.d2-3057089836 .stroke-B3{stroke:#E3E9FD;}
|
||||||
.d2-916646398 .stroke-B4{stroke:#E3E9FD;}
|
.d2-3057089836 .stroke-B4{stroke:#E3E9FD;}
|
||||||
.d2-916646398 .stroke-B5{stroke:#EDF0FD;}
|
.d2-3057089836 .stroke-B5{stroke:#EDF0FD;}
|
||||||
.d2-916646398 .stroke-B6{stroke:#F7F8FE;}
|
.d2-3057089836 .stroke-B6{stroke:#F7F8FE;}
|
||||||
.d2-916646398 .stroke-AA2{stroke:#4A6FF3;}
|
.d2-3057089836 .stroke-AA2{stroke:#4A6FF3;}
|
||||||
.d2-916646398 .stroke-AA4{stroke:#EDF0FD;}
|
.d2-3057089836 .stroke-AA4{stroke:#EDF0FD;}
|
||||||
.d2-916646398 .stroke-AA5{stroke:#F7F8FE;}
|
.d2-3057089836 .stroke-AA5{stroke:#F7F8FE;}
|
||||||
.d2-916646398 .stroke-AB4{stroke:#EDF0FD;}
|
.d2-3057089836 .stroke-AB4{stroke:#EDF0FD;}
|
||||||
.d2-916646398 .stroke-AB5{stroke:#F7F8FE;}
|
.d2-3057089836 .stroke-AB5{stroke:#F7F8FE;}
|
||||||
.d2-916646398 .background-color-N1{background-color:#0A0F25;}
|
.d2-3057089836 .background-color-N1{background-color:#0A0F25;}
|
||||||
.d2-916646398 .background-color-N2{background-color:#676C7E;}
|
.d2-3057089836 .background-color-N2{background-color:#676C7E;}
|
||||||
.d2-916646398 .background-color-N3{background-color:#9499AB;}
|
.d2-3057089836 .background-color-N3{background-color:#9499AB;}
|
||||||
.d2-916646398 .background-color-N4{background-color:#CFD2DD;}
|
.d2-3057089836 .background-color-N4{background-color:#CFD2DD;}
|
||||||
.d2-916646398 .background-color-N5{background-color:#DEE1EB;}
|
.d2-3057089836 .background-color-N5{background-color:#DEE1EB;}
|
||||||
.d2-916646398 .background-color-N6{background-color:#EEF1F8;}
|
.d2-3057089836 .background-color-N6{background-color:#EEF1F8;}
|
||||||
.d2-916646398 .background-color-N7{background-color:#FFFFFF;}
|
.d2-3057089836 .background-color-N7{background-color:#FFFFFF;}
|
||||||
.d2-916646398 .background-color-B1{background-color:#0D32B2;}
|
.d2-3057089836 .background-color-B1{background-color:#0D32B2;}
|
||||||
.d2-916646398 .background-color-B2{background-color:#0D32B2;}
|
.d2-3057089836 .background-color-B2{background-color:#0D32B2;}
|
||||||
.d2-916646398 .background-color-B3{background-color:#E3E9FD;}
|
.d2-3057089836 .background-color-B3{background-color:#E3E9FD;}
|
||||||
.d2-916646398 .background-color-B4{background-color:#E3E9FD;}
|
.d2-3057089836 .background-color-B4{background-color:#E3E9FD;}
|
||||||
.d2-916646398 .background-color-B5{background-color:#EDF0FD;}
|
.d2-3057089836 .background-color-B5{background-color:#EDF0FD;}
|
||||||
.d2-916646398 .background-color-B6{background-color:#F7F8FE;}
|
.d2-3057089836 .background-color-B6{background-color:#F7F8FE;}
|
||||||
.d2-916646398 .background-color-AA2{background-color:#4A6FF3;}
|
.d2-3057089836 .background-color-AA2{background-color:#4A6FF3;}
|
||||||
.d2-916646398 .background-color-AA4{background-color:#EDF0FD;}
|
.d2-3057089836 .background-color-AA4{background-color:#EDF0FD;}
|
||||||
.d2-916646398 .background-color-AA5{background-color:#F7F8FE;}
|
.d2-3057089836 .background-color-AA5{background-color:#F7F8FE;}
|
||||||
.d2-916646398 .background-color-AB4{background-color:#EDF0FD;}
|
.d2-3057089836 .background-color-AB4{background-color:#EDF0FD;}
|
||||||
.d2-916646398 .background-color-AB5{background-color:#F7F8FE;}
|
.d2-3057089836 .background-color-AB5{background-color:#F7F8FE;}
|
||||||
.d2-916646398 .color-N1{color:#0A0F25;}
|
.d2-3057089836 .color-N1{color:#0A0F25;}
|
||||||
.d2-916646398 .color-N2{color:#676C7E;}
|
.d2-3057089836 .color-N2{color:#676C7E;}
|
||||||
.d2-916646398 .color-N3{color:#9499AB;}
|
.d2-3057089836 .color-N3{color:#9499AB;}
|
||||||
.d2-916646398 .color-N4{color:#CFD2DD;}
|
.d2-3057089836 .color-N4{color:#CFD2DD;}
|
||||||
.d2-916646398 .color-N5{color:#DEE1EB;}
|
.d2-3057089836 .color-N5{color:#DEE1EB;}
|
||||||
.d2-916646398 .color-N6{color:#EEF1F8;}
|
.d2-3057089836 .color-N6{color:#EEF1F8;}
|
||||||
.d2-916646398 .color-N7{color:#FFFFFF;}
|
.d2-3057089836 .color-N7{color:#FFFFFF;}
|
||||||
.d2-916646398 .color-B1{color:#0D32B2;}
|
.d2-3057089836 .color-B1{color:#0D32B2;}
|
||||||
.d2-916646398 .color-B2{color:#0D32B2;}
|
.d2-3057089836 .color-B2{color:#0D32B2;}
|
||||||
.d2-916646398 .color-B3{color:#E3E9FD;}
|
.d2-3057089836 .color-B3{color:#E3E9FD;}
|
||||||
.d2-916646398 .color-B4{color:#E3E9FD;}
|
.d2-3057089836 .color-B4{color:#E3E9FD;}
|
||||||
.d2-916646398 .color-B5{color:#EDF0FD;}
|
.d2-3057089836 .color-B5{color:#EDF0FD;}
|
||||||
.d2-916646398 .color-B6{color:#F7F8FE;}
|
.d2-3057089836 .color-B6{color:#F7F8FE;}
|
||||||
.d2-916646398 .color-AA2{color:#4A6FF3;}
|
.d2-3057089836 .color-AA2{color:#4A6FF3;}
|
||||||
.d2-916646398 .color-AA4{color:#EDF0FD;}
|
.d2-3057089836 .color-AA4{color:#EDF0FD;}
|
||||||
.d2-916646398 .color-AA5{color:#F7F8FE;}
|
.d2-3057089836 .color-AA5{color:#F7F8FE;}
|
||||||
.d2-916646398 .color-AB4{color:#EDF0FD;}
|
.d2-3057089836 .color-AB4{color:#EDF0FD;}
|
||||||
.d2-916646398 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--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-bright);mix-blend-mode:darken}.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-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.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-bright);mix-blend-mode:darken}.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><a href="root.layers.x" xlink:href="root.layers.x"><g id="x"><g class="shape" ><rect x="0.000000" y="0.000000" width="85.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="42.500000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">x</text></g></a><g transform="translate(69 -16)" class="appendix-icon"><circle cx="16" cy="16" r="16" fill="white" stroke="#DEE1EB" /><text class="text-bold" x="16" y="21" style="font-size: 16px;text-anchor:middle;">1</text></g><mask id="d2-916646398" maskUnits="userSpaceOnUse" x="-101" y="-118" width="304" height="285">
|
.d2-3057089836 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--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-bright);mix-blend-mode:darken}.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-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.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-bright);mix-blend-mode:darken}.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><a href="root.layers.x" xlink:href="root.layers.x"><g id="x"><g class="shape" ><rect x="0.000000" y="0.000000" width="85.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="42.500000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">x</text></g></a><g transform="translate(69 -16)" class="appendix-icon"><circle cx="16" cy="16" r="16" fill="white" stroke="#DEE1EB" /><text class="text-bold" x="16" y="21" style="font-size: 16px;text-anchor:middle;">1</text></g><mask id="d2-3057089836" maskUnits="userSpaceOnUse" x="-101" y="-118" width="304" height="285">
|
||||||
<rect x="-101" y="-118" width="304" height="285" fill="white"></rect>
|
<rect x="-101" y="-118" width="304" height="285" fill="white"></rect>
|
||||||
<rect x="38.500000" y="22.500000" width="8" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
<rect x="38.500000" y="22.500000" width="8" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||||
</mask><line x1="-41.000000" x2="143.000000" y1="117.000000" y2="117.000000" class=" stroke-B2" /><g class="appendix" x="-1" y="67" width="104" height="100%"><g transform="translate(0 167)" class="appendix-icon"><circle cx="16" cy="0" r="16" fill="white" stroke="#DEE1EB" /><text class="text-bold" x="16" y="5" style="font-size: 16px;text-anchor:middle;">1</text></g><text class="text" x="48" y="172" style="font-size: 16px;">root > x</text></g>
|
</mask><line x1="-41.000000" x2="143.000000" y1="117.000000" y2="117.000000" class=" stroke-B2" /><g class="appendix" x="-1" y="67" width="104" height="100%"><g transform="translate(0 167)" class="appendix-icon"><circle cx="16" cy="0" r="16" fill="white" stroke="#DEE1EB" /><text class="text-bold" x="16" y="5" style="font-size: 16px;text-anchor:middle;">1</text></g><text class="text" x="48" y="172" style="font-size: 16px;">root > x</text></g>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 657 KiB After Width: | Height: | Size: 657 KiB |
|
Before Width: | Height: | Size: 662 KiB After Width: | Height: | Size: 662 KiB |
|
Before Width: | Height: | Size: 662 KiB After Width: | Height: | Size: 662 KiB |
|
Before Width: | Height: | Size: 661 KiB After Width: | Height: | Size: 661 KiB |
|
Before Width: | Height: | Size: 661 KiB After Width: | Height: | Size: 661 KiB |
|
|
@ -69,10 +69,10 @@ var grain string
|
||||||
var paper string
|
var paper string
|
||||||
|
|
||||||
type RenderOpts struct {
|
type RenderOpts struct {
|
||||||
Pad int
|
Pad *int64
|
||||||
Sketch bool
|
Sketch *bool
|
||||||
Center bool
|
Center *bool
|
||||||
ThemeID int64
|
ThemeID *int64
|
||||||
DarkThemeID *int64
|
DarkThemeID *int64
|
||||||
Font string
|
Font string
|
||||||
// the svg will be scaled by this factor, if unset the svg will fit to screen
|
// the svg will be scaled by this factor, if unset the svg will fit to screen
|
||||||
|
|
@ -1201,9 +1201,11 @@ func drawShape(writer, appendixWriter io.Writer, diagramHash string, targetShape
|
||||||
offsetY /= 2
|
offsetY /= 2
|
||||||
}
|
}
|
||||||
box.TopLeft.Y -= float64(offsetY)
|
box.TopLeft.Y -= float64(offsetY)
|
||||||
|
box.Height += float64(offsetY)
|
||||||
box.Width += d2target.THREE_DEE_OFFSET
|
box.Width += d2target.THREE_DEE_OFFSET
|
||||||
} else if targetShape.Multiple {
|
} else if targetShape.Multiple {
|
||||||
box.TopLeft.Y -= d2target.MULTIPLE_OFFSET
|
box.TopLeft.Y -= d2target.MULTIPLE_OFFSET
|
||||||
|
box.Height += d2target.MULTIPLE_OFFSET
|
||||||
box.Width += d2target.MULTIPLE_OFFSET
|
box.Width += d2target.MULTIPLE_OFFSET
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1218,13 +1220,12 @@ func drawShape(writer, appendixWriter io.Writer, diagramHash string, targetShape
|
||||||
fontClass := "text"
|
fontClass := "text"
|
||||||
if targetShape.FontFamily == "mono" {
|
if targetShape.FontFamily == "mono" {
|
||||||
fontClass = "text-mono"
|
fontClass = "text-mono"
|
||||||
} else {
|
}
|
||||||
if targetShape.Bold {
|
if targetShape.Bold {
|
||||||
fontClass += "-bold"
|
fontClass += "-bold"
|
||||||
} else if targetShape.Italic {
|
} else if targetShape.Italic {
|
||||||
fontClass += "-italic"
|
fontClass += "-italic"
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if targetShape.Underline {
|
if targetShape.Underline {
|
||||||
fontClass += " text-underline"
|
fontClass += " text-underline"
|
||||||
}
|
}
|
||||||
|
|
@ -1340,7 +1341,11 @@ func drawShape(writer, appendixWriter io.Writer, diagramHash string, targetShape
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if targetShape.Tooltip != "" {
|
||||||
|
fmt.Fprintf(writer, `<title>%s</title>`,
|
||||||
|
svg.EscapeText(targetShape.Tooltip),
|
||||||
|
)
|
||||||
|
}
|
||||||
addAppendixItems(appendixWriter, targetShape, s)
|
addAppendixItems(appendixWriter, targetShape, s)
|
||||||
|
|
||||||
fmt.Fprint(writer, closingTag)
|
fmt.Fprint(writer, closingTag)
|
||||||
|
|
@ -1389,12 +1394,12 @@ func addAppendixItems(writer io.Writer, targetShape d2target.Shape, s shape.Shap
|
||||||
x := int(math.Ceil(p1.X))
|
x := int(math.Ceil(p1.X))
|
||||||
y := int(math.Ceil(p1.Y))
|
y := int(math.Ceil(p1.Y))
|
||||||
|
|
||||||
fmt.Fprintf(writer, `<g transform="translate(%d %d)" class="appendix-icon">%s</g>`,
|
fmt.Fprintf(writer, `<g transform="translate(%d %d)" class="appendix-icon"><title>%s</title>%s</g>`,
|
||||||
x-appendixIconRadius,
|
x-appendixIconRadius,
|
||||||
y-appendixIconRadius,
|
y-appendixIconRadius,
|
||||||
|
svg.EscapeText(targetShape.Tooltip),
|
||||||
TooltipIcon,
|
TooltipIcon,
|
||||||
)
|
)
|
||||||
fmt.Fprintf(writer, `<title>%s</title>`, svg.EscapeText(targetShape.Tooltip))
|
|
||||||
}
|
}
|
||||||
if targetShape.Link != "" {
|
if targetShape.Link != "" {
|
||||||
if p2 == nil {
|
if p2 == nil {
|
||||||
|
|
@ -1687,26 +1692,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
|
var DEFAULT_DARK_THEME *int64 = nil // no theme selected
|
||||||
|
|
||||||
func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
|
func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
|
||||||
var sketchRunner *d2sketch.Runner
|
var sketchRunner *d2sketch.Runner
|
||||||
pad := DEFAULT_PADDING
|
pad := DEFAULT_PADDING
|
||||||
themeID := DEFAULT_THEME
|
themeID := d2themescatalog.NeutralDefault.ID
|
||||||
darkThemeID := DEFAULT_DARK_THEME
|
darkThemeID := DEFAULT_DARK_THEME
|
||||||
var scale *float64
|
var scale *float64
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
pad = opts.Pad
|
if opts.Pad != nil {
|
||||||
if opts.Sketch {
|
pad = int(*opts.Pad)
|
||||||
|
}
|
||||||
|
if opts.Sketch != nil && *opts.Sketch {
|
||||||
var err error
|
var err error
|
||||||
sketchRunner, err = d2sketch.InitSketchVM()
|
sketchRunner, err = d2sketch.InitSketchVM()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
themeID = opts.ThemeID
|
if opts.ThemeID != nil {
|
||||||
|
themeID = *opts.ThemeID
|
||||||
|
}
|
||||||
darkThemeID = opts.DarkThemeID
|
darkThemeID = opts.DarkThemeID
|
||||||
scale = opts.Scale
|
scale = opts.Scale
|
||||||
}
|
}
|
||||||
|
|
@ -1790,7 +1797,7 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
|
||||||
upperBuf := &bytes.Buffer{}
|
upperBuf := &bytes.Buffer{}
|
||||||
if opts.MasterID == "" {
|
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`
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -1911,7 +1918,7 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
alignment := "xMinYMin"
|
alignment := "xMinYMin"
|
||||||
if opts.Center {
|
if opts.Center != nil && *opts.Center {
|
||||||
alignment = "xMidYMid"
|
alignment = "xMidYMid"
|
||||||
}
|
}
|
||||||
fitToScreenWrapperOpening := ""
|
fitToScreenWrapperOpening := ""
|
||||||
|
|
@ -1952,8 +1959,11 @@ func Render(diagram *d2target.Diagram, opts *RenderOpts) ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO include only colors that are being used to reduce size
|
// TODO include only colors that are being used to reduce size
|
||||||
func ThemeCSS(diagramHash string, themeID int64, darkThemeID *int64) (stylesheet string, err error) {
|
func ThemeCSS(diagramHash string, themeID *int64, darkThemeID *int64) (stylesheet string, err error) {
|
||||||
out, err := singleThemeRulesets(diagramHash, themeID)
|
if themeID == nil {
|
||||||
|
themeID = &d2themescatalog.NeutralDefault.ID
|
||||||
|
}
|
||||||
|
out, err := singleThemeRulesets(diagramHash, *themeID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"oss.terrastruct.com/util-go/diff"
|
"oss.terrastruct.com/util-go/diff"
|
||||||
"oss.terrastruct.com/util-go/go2"
|
"oss.terrastruct.com/util-go/go2"
|
||||||
|
|
||||||
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||||
"oss.terrastruct.com/d2/d2lib"
|
"oss.terrastruct.com/d2/d2lib"
|
||||||
"oss.terrastruct.com/d2/d2renderers/d2fonts"
|
"oss.terrastruct.com/d2/d2renderers/d2fonts"
|
||||||
|
|
@ -426,12 +427,18 @@ func run(t *testing.T, tc testCase) {
|
||||||
return
|
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{
|
diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
|
||||||
Ruler: ruler,
|
Ruler: ruler,
|
||||||
Layout: d2dagrelayout.DefaultLayout,
|
LayoutResolver: layoutResolver,
|
||||||
FontFamily: go2.Pointer(d2fonts.HandDrawn),
|
FontFamily: go2.Pointer(d2fonts.HandDrawn),
|
||||||
ThemeID: 200,
|
}, renderOpts)
|
||||||
})
|
|
||||||
if !tassert.Nil(t, err) {
|
if !tassert.Nil(t, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -439,10 +446,7 @@ func run(t *testing.T, tc testCase) {
|
||||||
dataPath := filepath.Join("testdata", strings.TrimPrefix(t.Name(), "TestDarkTheme/"))
|
dataPath := filepath.Join("testdata", strings.TrimPrefix(t.Name(), "TestDarkTheme/"))
|
||||||
pathGotSVG := filepath.Join(dataPath, "dark_theme.got.svg")
|
pathGotSVG := filepath.Join(dataPath, "dark_theme.got.svg")
|
||||||
|
|
||||||
svgBytes, err := d2svg.Render(diagram, &d2svg.RenderOpts{
|
svgBytes, err := d2svg.Render(diagram, renderOpts)
|
||||||
Pad: d2svg.DEFAULT_PADDING,
|
|
||||||
ThemeID: 200,
|
|
||||||
})
|
|
||||||
assert.Success(t, err)
|
assert.Success(t, err)
|
||||||
err = os.MkdirAll(dataPath, 0755)
|
err = os.MkdirAll(dataPath, 0755)
|
||||||
assert.Success(t, err)
|
assert.Success(t, err)
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"hash/fnv"
|
"hash/fnv"
|
||||||
"math"
|
"math"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"oss.terrastruct.com/util-go/go2"
|
"oss.terrastruct.com/util-go/go2"
|
||||||
|
|
@ -38,8 +39,24 @@ const (
|
||||||
|
|
||||||
var BorderOffset = geo.NewVector(5, 5)
|
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 {
|
type Diagram struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
Config *Config `json:"config,omitempty"`
|
||||||
// See docs on the same field in d2graph to understand what it means.
|
// See docs on the same field in d2graph to understand what it means.
|
||||||
IsFolderOnly bool `json:"isFolderOnly"`
|
IsFolderOnly bool `json:"isFolderOnly"`
|
||||||
Description string `json:"description,omitempty"`
|
Description string `json:"description,omitempty"`
|
||||||
|
|
@ -56,6 +73,76 @@ type Diagram struct {
|
||||||
Steps []*Diagram `json:"steps,omitempty"`
|
Steps []*Diagram `json:"steps,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// boardPath comes in the form of "x/layers/z/scenarios/a"
|
||||||
|
// or in the form of "layers/z/scenarios/a"
|
||||||
|
func (d *Diagram) GetBoard(boardPath string) *Diagram {
|
||||||
|
path := strings.Split(boardPath, string(os.PathSeparator))
|
||||||
|
if len(path) == 0 || len(boardPath) == 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.getBoard(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Diagram) getBoard(boardPath []string) *Diagram {
|
||||||
|
if len(boardPath) == 0 {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
head := boardPath[0]
|
||||||
|
|
||||||
|
if head == "index" {
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
switch head {
|
||||||
|
case "layers":
|
||||||
|
if len(boardPath) < 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, b := range d.Layers {
|
||||||
|
if b.Name == boardPath[1] {
|
||||||
|
return b.getBoard(boardPath[2:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "scenarios":
|
||||||
|
if len(boardPath) < 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, b := range d.Scenarios {
|
||||||
|
if b.Name == boardPath[1] {
|
||||||
|
return b.getBoard(boardPath[2:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "steps":
|
||||||
|
if len(boardPath) < 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, b := range d.Steps {
|
||||||
|
if b.Name == boardPath[1] {
|
||||||
|
return b.getBoard(boardPath[2:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, b := range d.Layers {
|
||||||
|
if b.Name == head {
|
||||||
|
return b.getBoard(boardPath[2:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, b := range d.Scenarios {
|
||||||
|
if b.Name == head {
|
||||||
|
return b.getBoard(boardPath[2:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, b := range d.Steps {
|
||||||
|
if b.Name == head {
|
||||||
|
return b.getBoard(boardPath[2:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (diagram Diagram) Bytes() ([]byte, error) {
|
func (diagram Diagram) Bytes() ([]byte, error) {
|
||||||
b1, err := json.Marshal(diagram.Shapes)
|
b1, err := json.Marshal(diagram.Shapes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -65,7 +152,19 @@ func (diagram Diagram) Bytes() ([]byte, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
base := append(b1, b2...)
|
b3, err := json.Marshal(diagram.Root)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
base := append(append(b1, b2...), b3...)
|
||||||
|
|
||||||
|
if diagram.Config != nil {
|
||||||
|
b, err := json.Marshal(diagram.Config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
base = append(base, b...)
|
||||||
|
}
|
||||||
|
|
||||||
for _, d := range diagram.Layers {
|
for _, d := range diagram.Layers {
|
||||||
slices, err := d.Bytes()
|
slices, err := d.Bytes()
|
||||||
|
|
@ -199,11 +298,11 @@ func (diagram Diagram) BoundingBox() (topLeft, bottomRight Point) {
|
||||||
if strings.HasPrefix(targetShape.IconPosition, "OUTSIDE_TOP") {
|
if strings.HasPrefix(targetShape.IconPosition, "OUTSIDE_TOP") {
|
||||||
y1 = go2.Min(y1, targetShape.Pos.Y-label.PADDING-size)
|
y1 = go2.Min(y1, targetShape.Pos.Y-label.PADDING-size)
|
||||||
} else if strings.HasPrefix(targetShape.IconPosition, "OUTSIDE_BOTTOM") {
|
} else if strings.HasPrefix(targetShape.IconPosition, "OUTSIDE_BOTTOM") {
|
||||||
y2 = go2.Max(y2, targetShape.Pos.Y+label.PADDING+size)
|
y2 = go2.Max(y2, targetShape.Pos.Y+targetShape.Height+label.PADDING+size)
|
||||||
} else if strings.HasPrefix(targetShape.IconPosition, "OUTSIDE_LEFT") {
|
} else if strings.HasPrefix(targetShape.IconPosition, "OUTSIDE_LEFT") {
|
||||||
x1 = go2.Min(x1, targetShape.Pos.X-label.PADDING-size)
|
x1 = go2.Min(x1, targetShape.Pos.X-label.PADDING-size)
|
||||||
} else if strings.HasPrefix(targetShape.IconPosition, "OUTSIDE_RIGHT") {
|
} else if strings.HasPrefix(targetShape.IconPosition, "OUTSIDE_RIGHT") {
|
||||||
x2 = go2.Max(x2, targetShape.Pos.X+label.PADDING+size)
|
x2 = go2.Max(x2, targetShape.Pos.X+targetShape.Width+label.PADDING+size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,21 +11,24 @@ import (
|
||||||
"oss.terrastruct.com/d2/d2renderers/d2svg"
|
"oss.terrastruct.com/d2/d2renderers/d2svg"
|
||||||
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
|
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
|
||||||
"oss.terrastruct.com/d2/lib/textmeasure"
|
"oss.terrastruct.com/d2/lib/textmeasure"
|
||||||
|
"oss.terrastruct.com/util-go/go2"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Remember to add if err != nil checks in production.
|
// Remember to add if err != nil checks in production.
|
||||||
func main() {
|
func main() {
|
||||||
ruler, _ := textmeasure.NewRuler()
|
ruler, _ := textmeasure.NewRuler()
|
||||||
defaultLayout := func(ctx context.Context, g *d2graph.Graph) error {
|
layoutResolver := func(engine string) (d2graph.LayoutGraph, error) {
|
||||||
return d2dagrelayout.Layout(ctx, g, nil)
|
return d2dagrelayout.DefaultLayout, nil
|
||||||
}
|
}
|
||||||
diagram, _, _ := d2lib.Compile(context.Background(), "x -> y", &d2lib.CompileOptions{
|
renderOpts := &d2svg.RenderOpts{
|
||||||
Layout: defaultLayout,
|
Pad: go2.Pointer(int64(5)),
|
||||||
|
ThemeID: &d2themescatalog.GrapeSoda.ID,
|
||||||
|
}
|
||||||
|
compileOpts := &d2lib.CompileOptions{
|
||||||
|
LayoutResolver: layoutResolver,
|
||||||
Ruler: ruler,
|
Ruler: ruler,
|
||||||
})
|
}
|
||||||
out, _ := d2svg.Render(diagram, &d2svg.RenderOpts{
|
diagram, _, _ := d2lib.Compile(context.Background(), "x -> y", compileOpts, renderOpts)
|
||||||
Pad: d2svg.DEFAULT_PADDING,
|
out, _ := d2svg.Render(diagram, renderOpts)
|
||||||
ThemeID: d2themescatalog.GrapeSoda.ID,
|
|
||||||
})
|
|
||||||
_ = ioutil.WriteFile(filepath.Join("out.svg"), out, 0600)
|
_ = ioutil.WriteFile(filepath.Join("out.svg"), out, 0600)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"oss.terrastruct.com/d2/d2format"
|
"oss.terrastruct.com/d2/d2format"
|
||||||
|
"oss.terrastruct.com/d2/d2graph"
|
||||||
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
"oss.terrastruct.com/d2/d2layouts/d2dagrelayout"
|
||||||
"oss.terrastruct.com/d2/d2lib"
|
"oss.terrastruct.com/d2/d2lib"
|
||||||
"oss.terrastruct.com/d2/d2oracle"
|
"oss.terrastruct.com/d2/d2oracle"
|
||||||
|
|
@ -15,10 +16,14 @@ import (
|
||||||
func main() {
|
func main() {
|
||||||
// From one.go
|
// From one.go
|
||||||
ruler, _ := textmeasure.NewRuler()
|
ruler, _ := textmeasure.NewRuler()
|
||||||
_, graph, _ := d2lib.Compile(context.Background(), "x -> y", &d2lib.CompileOptions{
|
layoutResolver := func(engine string) (d2graph.LayoutGraph, error) {
|
||||||
Layout: d2dagrelayout.DefaultLayout,
|
return d2dagrelayout.DefaultLayout, nil
|
||||||
|
}
|
||||||
|
compileOpts := &d2lib.CompileOptions{
|
||||||
|
LayoutResolver: layoutResolver,
|
||||||
Ruler: ruler,
|
Ruler: ruler,
|
||||||
})
|
}
|
||||||
|
_, graph, _ := d2lib.Compile(context.Background(), "x -> y", compileOpts, nil)
|
||||||
|
|
||||||
// Create a shape with the ID, "meow"
|
// Create a shape with the ID, "meow"
|
||||||
graph, _, _ = d2oracle.Create(graph, nil, "meow")
|
graph, _, _ = d2oracle.Create(graph, nil, "meow")
|
||||||
|
|
|
||||||
|
|
@ -16,15 +16,15 @@ import (
|
||||||
|
|
||||||
// Remember to add if err != nil checks in production.
|
// Remember to add if err != nil checks in production.
|
||||||
func main() {
|
func main() {
|
||||||
graph, _ := d2compiler.Compile("", strings.NewReader("x -> y"), nil)
|
graph, config, _ := d2compiler.Compile("", strings.NewReader("x -> y"), nil)
|
||||||
graph.ApplyTheme(d2themescatalog.NeutralDefault.ID)
|
graph.ApplyTheme(d2themescatalog.NeutralDefault.ID)
|
||||||
ruler, _ := textmeasure.NewRuler()
|
ruler, _ := textmeasure.NewRuler()
|
||||||
_ = graph.SetDimensions(nil, ruler, nil)
|
_ = graph.SetDimensions(nil, ruler, nil)
|
||||||
_ = d2dagrelayout.Layout(context.Background(), graph, nil)
|
_ = d2dagrelayout.Layout(context.Background(), graph, nil)
|
||||||
diagram, _ := d2exporter.Export(context.Background(), graph, nil)
|
diagram, _ := d2exporter.Export(context.Background(), graph, nil)
|
||||||
|
diagram.Config = config
|
||||||
out, _ := d2svg.Render(diagram, &d2svg.RenderOpts{
|
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)
|
_ = ioutil.WriteFile(filepath.Join("out.svg"), out, 0600)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ layers: {
|
||||||
link: steps.1
|
link: steps.1
|
||||||
style.font-size: 24
|
style.font-size: 24
|
||||||
}
|
}
|
||||||
|
|
||||||
steps: {
|
steps: {
|
||||||
1: {
|
1: {
|
||||||
titled: Earn pre-requisite titles (IM)
|
titled: Earn pre-requisite titles (IM)
|
||||||
|
|
|
||||||
|
|
@ -74,8 +74,17 @@ func TestCLI_E2E(t *testing.T) {
|
||||||
writeFile(t, dir, "empty-layer.d2", `layers: { x: {} }`)
|
writeFile(t, dir, "empty-layer.d2", `layers: { x: {} }`)
|
||||||
err := runTestMain(t, ctx, dir, env, "empty-layer.d2")
|
err := runTestMain(t, ctx, dir, env, "empty-layer.d2")
|
||||||
assert.Success(t, err)
|
assert.Success(t, err)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "layer-link",
|
||||||
|
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
|
||||||
|
writeFile(t, dir, "test.d2", `doh: { link: layers.test2 }; layers: { test2: @test2.d2 }`)
|
||||||
|
writeFile(t, dir, "test2.d2", `x: I'm a Mac { link: https://example.com }`)
|
||||||
|
err := runTestMain(t, ctx, dir, env, "test.d2", "layer-link.svg")
|
||||||
|
assert.Success(t, err)
|
||||||
|
|
||||||
assert.TestdataDir(t, filepath.Join(dir, "empty-layer"))
|
assert.TestdataDir(t, filepath.Join(dir, "layer-link"))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -111,6 +120,38 @@ func TestCLI_E2E(t *testing.T) {
|
||||||
shape: text
|
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: {
|
steps: {
|
||||||
1: {
|
1: {
|
||||||
Approach road
|
Approach road
|
||||||
|
|
@ -404,6 +445,17 @@ steps: {
|
||||||
assert.Testdata(t, ".svg", svg)
|
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",
|
name: "import_spread_nested",
|
||||||
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
|
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
|
||||||
|
|
@ -449,6 +501,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()
|
ctx := context.Background()
|
||||||
|
|
|
||||||
152
e2etests-cli/testdata/TestCLI_E2E/abspath.exp.svg
vendored
|
|
@ -1,9 +1,9 @@
|
||||||
<?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[
|
<?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.6.0-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 256 434"><svg id="d2-svg" class="d2-1843626214" 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 {
|
.d2-1843626214 .text-bold {
|
||||||
font-family: "d2-855222762-font-bold";
|
font-family: "d2-1843626214-font-bold";
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: d2-855222762-font-bold;
|
font-family: d2-1843626214-font-bold;
|
||||||
src: url("data:application/font-woff;base64,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");
|
src: url("data:application/font-woff;base64,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");
|
||||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||||
shape-rendering: geometricPrecision;
|
shape-rendering: geometricPrecision;
|
||||||
|
|
@ -18,78 +18,78 @@
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2-855222762 .fill-N1{fill:#0A0F25;}
|
.d2-1843626214 .fill-N1{fill:#0A0F25;}
|
||||||
.d2-855222762 .fill-N2{fill:#676C7E;}
|
.d2-1843626214 .fill-N2{fill:#676C7E;}
|
||||||
.d2-855222762 .fill-N3{fill:#9499AB;}
|
.d2-1843626214 .fill-N3{fill:#9499AB;}
|
||||||
.d2-855222762 .fill-N4{fill:#CFD2DD;}
|
.d2-1843626214 .fill-N4{fill:#CFD2DD;}
|
||||||
.d2-855222762 .fill-N5{fill:#DEE1EB;}
|
.d2-1843626214 .fill-N5{fill:#DEE1EB;}
|
||||||
.d2-855222762 .fill-N6{fill:#EEF1F8;}
|
.d2-1843626214 .fill-N6{fill:#EEF1F8;}
|
||||||
.d2-855222762 .fill-N7{fill:#FFFFFF;}
|
.d2-1843626214 .fill-N7{fill:#FFFFFF;}
|
||||||
.d2-855222762 .fill-B1{fill:#0D32B2;}
|
.d2-1843626214 .fill-B1{fill:#0D32B2;}
|
||||||
.d2-855222762 .fill-B2{fill:#0D32B2;}
|
.d2-1843626214 .fill-B2{fill:#0D32B2;}
|
||||||
.d2-855222762 .fill-B3{fill:#E3E9FD;}
|
.d2-1843626214 .fill-B3{fill:#E3E9FD;}
|
||||||
.d2-855222762 .fill-B4{fill:#E3E9FD;}
|
.d2-1843626214 .fill-B4{fill:#E3E9FD;}
|
||||||
.d2-855222762 .fill-B5{fill:#EDF0FD;}
|
.d2-1843626214 .fill-B5{fill:#EDF0FD;}
|
||||||
.d2-855222762 .fill-B6{fill:#F7F8FE;}
|
.d2-1843626214 .fill-B6{fill:#F7F8FE;}
|
||||||
.d2-855222762 .fill-AA2{fill:#4A6FF3;}
|
.d2-1843626214 .fill-AA2{fill:#4A6FF3;}
|
||||||
.d2-855222762 .fill-AA4{fill:#EDF0FD;}
|
.d2-1843626214 .fill-AA4{fill:#EDF0FD;}
|
||||||
.d2-855222762 .fill-AA5{fill:#F7F8FE;}
|
.d2-1843626214 .fill-AA5{fill:#F7F8FE;}
|
||||||
.d2-855222762 .fill-AB4{fill:#EDF0FD;}
|
.d2-1843626214 .fill-AB4{fill:#EDF0FD;}
|
||||||
.d2-855222762 .fill-AB5{fill:#F7F8FE;}
|
.d2-1843626214 .fill-AB5{fill:#F7F8FE;}
|
||||||
.d2-855222762 .stroke-N1{stroke:#0A0F25;}
|
.d2-1843626214 .stroke-N1{stroke:#0A0F25;}
|
||||||
.d2-855222762 .stroke-N2{stroke:#676C7E;}
|
.d2-1843626214 .stroke-N2{stroke:#676C7E;}
|
||||||
.d2-855222762 .stroke-N3{stroke:#9499AB;}
|
.d2-1843626214 .stroke-N3{stroke:#9499AB;}
|
||||||
.d2-855222762 .stroke-N4{stroke:#CFD2DD;}
|
.d2-1843626214 .stroke-N4{stroke:#CFD2DD;}
|
||||||
.d2-855222762 .stroke-N5{stroke:#DEE1EB;}
|
.d2-1843626214 .stroke-N5{stroke:#DEE1EB;}
|
||||||
.d2-855222762 .stroke-N6{stroke:#EEF1F8;}
|
.d2-1843626214 .stroke-N6{stroke:#EEF1F8;}
|
||||||
.d2-855222762 .stroke-N7{stroke:#FFFFFF;}
|
.d2-1843626214 .stroke-N7{stroke:#FFFFFF;}
|
||||||
.d2-855222762 .stroke-B1{stroke:#0D32B2;}
|
.d2-1843626214 .stroke-B1{stroke:#0D32B2;}
|
||||||
.d2-855222762 .stroke-B2{stroke:#0D32B2;}
|
.d2-1843626214 .stroke-B2{stroke:#0D32B2;}
|
||||||
.d2-855222762 .stroke-B3{stroke:#E3E9FD;}
|
.d2-1843626214 .stroke-B3{stroke:#E3E9FD;}
|
||||||
.d2-855222762 .stroke-B4{stroke:#E3E9FD;}
|
.d2-1843626214 .stroke-B4{stroke:#E3E9FD;}
|
||||||
.d2-855222762 .stroke-B5{stroke:#EDF0FD;}
|
.d2-1843626214 .stroke-B5{stroke:#EDF0FD;}
|
||||||
.d2-855222762 .stroke-B6{stroke:#F7F8FE;}
|
.d2-1843626214 .stroke-B6{stroke:#F7F8FE;}
|
||||||
.d2-855222762 .stroke-AA2{stroke:#4A6FF3;}
|
.d2-1843626214 .stroke-AA2{stroke:#4A6FF3;}
|
||||||
.d2-855222762 .stroke-AA4{stroke:#EDF0FD;}
|
.d2-1843626214 .stroke-AA4{stroke:#EDF0FD;}
|
||||||
.d2-855222762 .stroke-AA5{stroke:#F7F8FE;}
|
.d2-1843626214 .stroke-AA5{stroke:#F7F8FE;}
|
||||||
.d2-855222762 .stroke-AB4{stroke:#EDF0FD;}
|
.d2-1843626214 .stroke-AB4{stroke:#EDF0FD;}
|
||||||
.d2-855222762 .stroke-AB5{stroke:#F7F8FE;}
|
.d2-1843626214 .stroke-AB5{stroke:#F7F8FE;}
|
||||||
.d2-855222762 .background-color-N1{background-color:#0A0F25;}
|
.d2-1843626214 .background-color-N1{background-color:#0A0F25;}
|
||||||
.d2-855222762 .background-color-N2{background-color:#676C7E;}
|
.d2-1843626214 .background-color-N2{background-color:#676C7E;}
|
||||||
.d2-855222762 .background-color-N3{background-color:#9499AB;}
|
.d2-1843626214 .background-color-N3{background-color:#9499AB;}
|
||||||
.d2-855222762 .background-color-N4{background-color:#CFD2DD;}
|
.d2-1843626214 .background-color-N4{background-color:#CFD2DD;}
|
||||||
.d2-855222762 .background-color-N5{background-color:#DEE1EB;}
|
.d2-1843626214 .background-color-N5{background-color:#DEE1EB;}
|
||||||
.d2-855222762 .background-color-N6{background-color:#EEF1F8;}
|
.d2-1843626214 .background-color-N6{background-color:#EEF1F8;}
|
||||||
.d2-855222762 .background-color-N7{background-color:#FFFFFF;}
|
.d2-1843626214 .background-color-N7{background-color:#FFFFFF;}
|
||||||
.d2-855222762 .background-color-B1{background-color:#0D32B2;}
|
.d2-1843626214 .background-color-B1{background-color:#0D32B2;}
|
||||||
.d2-855222762 .background-color-B2{background-color:#0D32B2;}
|
.d2-1843626214 .background-color-B2{background-color:#0D32B2;}
|
||||||
.d2-855222762 .background-color-B3{background-color:#E3E9FD;}
|
.d2-1843626214 .background-color-B3{background-color:#E3E9FD;}
|
||||||
.d2-855222762 .background-color-B4{background-color:#E3E9FD;}
|
.d2-1843626214 .background-color-B4{background-color:#E3E9FD;}
|
||||||
.d2-855222762 .background-color-B5{background-color:#EDF0FD;}
|
.d2-1843626214 .background-color-B5{background-color:#EDF0FD;}
|
||||||
.d2-855222762 .background-color-B6{background-color:#F7F8FE;}
|
.d2-1843626214 .background-color-B6{background-color:#F7F8FE;}
|
||||||
.d2-855222762 .background-color-AA2{background-color:#4A6FF3;}
|
.d2-1843626214 .background-color-AA2{background-color:#4A6FF3;}
|
||||||
.d2-855222762 .background-color-AA4{background-color:#EDF0FD;}
|
.d2-1843626214 .background-color-AA4{background-color:#EDF0FD;}
|
||||||
.d2-855222762 .background-color-AA5{background-color:#F7F8FE;}
|
.d2-1843626214 .background-color-AA5{background-color:#F7F8FE;}
|
||||||
.d2-855222762 .background-color-AB4{background-color:#EDF0FD;}
|
.d2-1843626214 .background-color-AB4{background-color:#EDF0FD;}
|
||||||
.d2-855222762 .background-color-AB5{background-color:#F7F8FE;}
|
.d2-1843626214 .background-color-AB5{background-color:#F7F8FE;}
|
||||||
.d2-855222762 .color-N1{color:#0A0F25;}
|
.d2-1843626214 .color-N1{color:#0A0F25;}
|
||||||
.d2-855222762 .color-N2{color:#676C7E;}
|
.d2-1843626214 .color-N2{color:#676C7E;}
|
||||||
.d2-855222762 .color-N3{color:#9499AB;}
|
.d2-1843626214 .color-N3{color:#9499AB;}
|
||||||
.d2-855222762 .color-N4{color:#CFD2DD;}
|
.d2-1843626214 .color-N4{color:#CFD2DD;}
|
||||||
.d2-855222762 .color-N5{color:#DEE1EB;}
|
.d2-1843626214 .color-N5{color:#DEE1EB;}
|
||||||
.d2-855222762 .color-N6{color:#EEF1F8;}
|
.d2-1843626214 .color-N6{color:#EEF1F8;}
|
||||||
.d2-855222762 .color-N7{color:#FFFFFF;}
|
.d2-1843626214 .color-N7{color:#FFFFFF;}
|
||||||
.d2-855222762 .color-B1{color:#0D32B2;}
|
.d2-1843626214 .color-B1{color:#0D32B2;}
|
||||||
.d2-855222762 .color-B2{color:#0D32B2;}
|
.d2-1843626214 .color-B2{color:#0D32B2;}
|
||||||
.d2-855222762 .color-B3{color:#E3E9FD;}
|
.d2-1843626214 .color-B3{color:#E3E9FD;}
|
||||||
.d2-855222762 .color-B4{color:#E3E9FD;}
|
.d2-1843626214 .color-B4{color:#E3E9FD;}
|
||||||
.d2-855222762 .color-B5{color:#EDF0FD;}
|
.d2-1843626214 .color-B5{color:#EDF0FD;}
|
||||||
.d2-855222762 .color-B6{color:#F7F8FE;}
|
.d2-1843626214 .color-B6{color:#F7F8FE;}
|
||||||
.d2-855222762 .color-AA2{color:#4A6FF3;}
|
.d2-1843626214 .color-AA2{color:#4A6FF3;}
|
||||||
.d2-855222762 .color-AA4{color:#EDF0FD;}
|
.d2-1843626214 .color-AA4{color:#EDF0FD;}
|
||||||
.d2-855222762 .color-AA5{color:#F7F8FE;}
|
.d2-1843626214 .color-AA5{color:#F7F8FE;}
|
||||||
.d2-855222762 .color-AB4{color:#EDF0FD;}
|
.d2-1843626214 .color-AB4{color:#EDF0FD;}
|
||||||
.d2-855222762 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--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-bright);mix-blend-mode:darken}.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-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.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-bright);mix-blend-mode:darken}.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><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">
|
.d2-1843626214 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--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-bright);mix-blend-mode:darken}.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-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.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-bright);mix-blend-mode:darken}.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><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-1843626214)" /></g><mask id="d2-1843626214" maskUnits="userSpaceOnUse" x="-101" y="-101" width="256" height="434">
|
||||||
<rect x="-101" y="-101" width="256" height="434" fill="white"></rect>
|
<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="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>
|
<rect x="22.500000" y="188.500000" width="9" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.5 KiB |
182
e2etests-cli/testdata/TestCLI_E2E/animation.exp.svg
vendored
|
|
@ -1,16 +1,16 @@
|
||||||
<?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 514 665"><svg id="d2-svg" width="514" height="665" viewBox="-206 -166 514 665"><style type="text/css"><![CDATA[
|
<?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.6.0-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 514 665"><svg id="d2-svg" width="514" height="665" viewBox="-206 -166 514 665"><style type="text/css"><![CDATA[
|
||||||
.d2-508224771 .text {
|
.d2-281690071 .text {
|
||||||
font-family: "d2-508224771-font-regular";
|
font-family: "d2-281690071-font-regular";
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: d2-508224771-font-regular;
|
font-family: d2-281690071-font-regular;
|
||||||
src: url("data:application/font-woff;base64,d09GRgABAAAAAAusAAoAAAAAEhQAAguFAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAAA9AAAAGAAAABgXd/Vo2NtYXAAAAFUAAAAkQAAAMADlQPxZ2x5ZgAAAegAAAVuAAAHBDysTkJoZWFkAAAHWAAAADYAAAA2G4Ue32hoZWEAAAeQAAAAJAAAACQKhAXaaG10eAAAB7QAAABgAAAAYCqBBP5sb2NhAAAIFAAAADIAAAAyF3QVqG1heHAAAAhIAAAAIAAAACAAMAD2bmFtZQAACGgAAAMjAAAIFAbDVU1wb3N0AAALjAAAAB0AAAAg/9EAMgADAgkBkAAFAAACigJYAAAASwKKAlgAAAFeADIBIwAAAgsFAwMEAwICBGAAAvcAAAADAAAAAAAAAABBREJPAEAAIP//Au7/BgAAA9gBESAAAZ8AAAAAAeYClAAAACAAA3icfM05SgMBAEbhb5xxH8dxa8XOc4i1hxARFEVEEfEsahaSIwTSJkfJBXKFPxBIkSav/YqHQqlArTLCpVapceXajVt37j169urdpy/ffhLW/MGTF28+Vp5Z5plmknGGGaSfXrrp5D9/+V3eNlW4sKVU2bZj1559Bw7VjjSOtU6cOnPOAgAA//8BAAD//zx3J24AAAB4nHSVXWzbah3G/+9rN05ad4mXDydtEid2GzdJ22RxErdN5qxt0nVd26ROq62fqGu3lJXBKNKmSmXjY2hXQC82MQkkEEwaSEgTTBog7jZNBAZDu2GAYOIqm+CCo5xeHOmcOkdO068jnbv3xs///T3P8/4NTTALgBP4HhBgAjOcBDuAxPiZTr8oCpQsybLAErKIGGoW/UvbRuhcnEwmyVND/xvavH0bXbyF7+1+aeBOqfRi6eZN7buV91oMvXoPGAgA7MHbYAIGwEpJYiAgCgYDYZWsgihQL7kX3EmfhTT7/vl26e2s8v8M+srqqnytv/+aNoe3d6+XywAACOK1HdyOfwQegCY+EEjEk0kp5mCpQEDgDQa7zeGQYkmZNRiQqn7z/PidYnrB3dM2FFIWpdi8EhnjesVL9NSD9asP1FO+pJsfvKGqm0NdfLwnVtefA8Bfx9u6vsRIVoeDlZJJ2SoxAhNPygJFCIQoOBx2Zm71Fs3SJG2nty5PGgkyviVvxUmCwtvaT/kcz+d4tLR7HX2xez18X/slmr4fXu/WfgAAWGdAv0JVaIMOAJbXIeR4HYAS6zh2RtDNEWNJOVGHenZ66vs/ZMJdoTGPj18ZmC1kKYKfcgiKsLkco88NFmYYrk/w2fodwWvz2t8G3KEhnrtrTkeCnYCgt7aDHqMquD/Ps33LTp5ZSw+uK9GcK2SPeLpzYnGYH3B0+At0eqOgbqR5Nml1Rmb6iiWPTfb4dZZIbQf9A5fBCr59lrq4mJD2IeTEwaCP5r+cWpZDio8sZinCPe46k+b6vWImMEJ/ZzP/NcXbVvz9bl+/O5gb1txspNh3YQVw/f5/QlVwAneMwG4zUP6DwAl/XB+D2MGrSmZVXryMsPbbpgsjQqrdw+VfIjLTL03RpzfyhQ1la63VZZpYsDNJmxcFxiby9exVAPQGl8FWz95O7WfB1IUpRlUJYSI2cVbtjnamOnH52ao/sryo/RkFs0qgU/sJ1GqQA4An+CkOgAMADMBuwYF2BZeBrmszklWirIJI2dUp4q/zP/vd3PfmcVnzIniu/fu/V7/R+Ka2A3/HZTDvOctIzEFUv+gNqidMJEW1GB10fwJf2b1nZRBSSHKfA1UbHHqBP8ORpQhh8gAEVUaE4xwNzz9AVTBD+zHPddP1YibqWnabA5lTpUymlEpfyWSupDMTExllcrLRl/SGWthIZ0vF6bW16WJJ11VrEvoYVRt9ObydzWAQ+IDI2q372pTd4dBv6s+Hly6lvtDHD/P4ZjqfynGZDr/yF/ykz91196vqDcXbNvMQGUpzhRXeV3Ozh34voaq+bQ48aDR+zwDXaNDDWmibmRt2ocrF3mTzKEnGFK2xZ9y1HfRtVIVQ3XtRrtcsEQ8ExF6ciB95P/rKYb1YB3gdXxKCvmw4GvVL7fxQaDbfM+nuciV9vWFvtF3I9gTztOiWXf4ezsWzza3+RDCV97FxqzPkZj32lla/3CsOddXnn6/toFeoomd4LHum8az+MzFaDEcDKV5n4cfp5UUU195kFTGMZrW28a4oIHAC4KeoAn4AiTiyyw5PhEDs7WGK+PHd6VHjCYo0WkznC+MmxkgazdTZyW+tjpjMJtJoac6iivaOH+b5YR65jpzaUJOQ7ezMCdongICuRdAfUEVvzaFvsnx0PHECz1k8tMVoMwWT5pbnMystrhayxdZ8ofAbJpJ7bSAHcVOqpwO90z7kRnn/qA+17laj4z26LwX0GH6Ofw1NAFZRlChqxUJcJCzo8aOFhUd7ucNDVNH/N/o7U1VU0doA1f6Ix0DGT6EFgKlvqb3SOTnO6eQ4POZxOb1ep8sDnwIAAP//AQAA///EanloAAAAAQAAAAILhYvQ0stfDzz1AAMD6AAAAADYXaChAAAAAN1mLzb+Ov7bCG8DyAAAAAMAAgAAAAAAAAABAAAD2P7vAAAImP46/joIbwABAAAAAAAAAAAAAAAAAAAAGAKNAFkAyAAAAiAAAwI7ADQC1wBaAfgANAHIAC4CKwAvAfAALgIgAFIA9gBFAe8AUgD/AFICIwBSAh4ALgIrAFIBWwBSAaMAHAIgAEsCzgAYAdMADAD5AFAA9gBSAAD/yQAAACwALABQAIAAsgDqARgBSgF+AaABrAHGAeICBAIwAmQChALEAuYDIANQA2ADbAOCAAAAAQAAABgAjAAMAGYABwABAAAAAAAAAAAAAAAAAAQAA3icnJTdThtXFIU/B9ttVDUXFYrIDTqXbZWM3QiiBK5MCYpVhFOP0x+pqjR4xj9iPDPyDFCqPkCv+xZ9i1z1OfoQVa+rs7wNNqoUgRCwzpy991lnr7UPsMm/bFCrPwT+av5guMZ2c8/wAx41nxre4Ljxt+H6SkyDuPGb4SZfNvqGP+J9/Q/DH7NT/9nwQ7bqR4Y/4Xl90/CnG45/DD9ih/cLXIOX/G64xhaF4Qds8pPhDR5jNWt1HtM23OAztg032QYGTKlImZIxxjFiyphz5iSUhCTMmTIiIcbRpUNKpa8ZkZBj/L9fI0Iq5kSqOKHCkRKSElEysYq/KivnrU4caTW3vQ4VEyJOlXFGRIYjZ0xORsKZ6lRUFOzRokXJUHwLKkoCSqakBOTMGdOixxHHDJgwpcRxpEqeWUjOiIpLIp3vLMJ3ZkhCRmmszsmIxdOJX6LsLsc4ehSKXa18vFbhKY7vlO255Yr9ikC/boXZ+rlLNhEX6meqrqTauZSCE+36czt8K1yxh7tXf9aZfLhHsf5XqnzKufSPpVQmJhnObdEhlINC9wTHgdZdQnXke7oMeEOPdwy07tCnT4cTBnR5rdwefRxf0+OEQ2V0hRd7R3LMCT/i+IauYnztxPqzUCzhFwpzdymOc91jRqGee+aB7prohndX2M9QvuaOUjlDzZGPdNIv05xFjM0VhRjO1MulN0rrX2yOmOkuXtubfT8NFzZ7yym+ItcMe7cuOHnlFow+pGpwyzOX+gmIiMk5VcSQnBktKq7E+y0R56Q4DtW9N5qSis51jj/nSi5JmIlBl0x15hT6G5lvQuM+XPO9s7ckVr5nenZ9q/uc4tSrG43eqXvLvdC6nKwo0DJV8xU3DcU1M+8nmqlV/qFyS71uOc/ok0j1VDe4/Q48J6DNDrvsM9E5Q+1c2BvR1jvR5hX76sEZiaJGcnViFXYJeMEuu7zixVrNDocc0GP/DhwXWT0OeH1rZ12nZRVndf4Um7b4Op5dr17eW6/P7+DLLzRRNy9jX9r4bl9YtRv/nxAx81zc1uqd3BOC/wAAAP//AQAA//8HW0wwAHicYmBmAIP/5xiMGLAAAAAAAP//AQAA//8vAQIDAAAA");
|
src: url("data:application/font-woff;base64,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");
|
||||||
}
|
}
|
||||||
.d2-508224771 .text-bold {
|
.d2-281690071 .text-bold {
|
||||||
font-family: "d2-508224771-font-bold";
|
font-family: "d2-281690071-font-bold";
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: d2-508224771-font-bold;
|
font-family: d2-281690071-font-bold;
|
||||||
src: url("data:application/font-woff;base64,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");
|
src: url("data:application/font-woff;base64,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");
|
||||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||||
shape-rendering: geometricPrecision;
|
shape-rendering: geometricPrecision;
|
||||||
|
|
@ -25,92 +25,92 @@
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2-508224771 .fill-N1{fill:#0A0F25;}
|
.d2-281690071 .fill-N1{fill:#0A0F25;}
|
||||||
.d2-508224771 .fill-N2{fill:#676C7E;}
|
.d2-281690071 .fill-N2{fill:#676C7E;}
|
||||||
.d2-508224771 .fill-N3{fill:#9499AB;}
|
.d2-281690071 .fill-N3{fill:#9499AB;}
|
||||||
.d2-508224771 .fill-N4{fill:#CFD2DD;}
|
.d2-281690071 .fill-N4{fill:#CFD2DD;}
|
||||||
.d2-508224771 .fill-N5{fill:#DEE1EB;}
|
.d2-281690071 .fill-N5{fill:#DEE1EB;}
|
||||||
.d2-508224771 .fill-N6{fill:#EEF1F8;}
|
.d2-281690071 .fill-N6{fill:#EEF1F8;}
|
||||||
.d2-508224771 .fill-N7{fill:#FFFFFF;}
|
.d2-281690071 .fill-N7{fill:#FFFFFF;}
|
||||||
.d2-508224771 .fill-B1{fill:#0D32B2;}
|
.d2-281690071 .fill-B1{fill:#0D32B2;}
|
||||||
.d2-508224771 .fill-B2{fill:#0D32B2;}
|
.d2-281690071 .fill-B2{fill:#0D32B2;}
|
||||||
.d2-508224771 .fill-B3{fill:#E3E9FD;}
|
.d2-281690071 .fill-B3{fill:#E3E9FD;}
|
||||||
.d2-508224771 .fill-B4{fill:#E3E9FD;}
|
.d2-281690071 .fill-B4{fill:#E3E9FD;}
|
||||||
.d2-508224771 .fill-B5{fill:#EDF0FD;}
|
.d2-281690071 .fill-B5{fill:#EDF0FD;}
|
||||||
.d2-508224771 .fill-B6{fill:#F7F8FE;}
|
.d2-281690071 .fill-B6{fill:#F7F8FE;}
|
||||||
.d2-508224771 .fill-AA2{fill:#4A6FF3;}
|
.d2-281690071 .fill-AA2{fill:#4A6FF3;}
|
||||||
.d2-508224771 .fill-AA4{fill:#EDF0FD;}
|
.d2-281690071 .fill-AA4{fill:#EDF0FD;}
|
||||||
.d2-508224771 .fill-AA5{fill:#F7F8FE;}
|
.d2-281690071 .fill-AA5{fill:#F7F8FE;}
|
||||||
.d2-508224771 .fill-AB4{fill:#EDF0FD;}
|
.d2-281690071 .fill-AB4{fill:#EDF0FD;}
|
||||||
.d2-508224771 .fill-AB5{fill:#F7F8FE;}
|
.d2-281690071 .fill-AB5{fill:#F7F8FE;}
|
||||||
.d2-508224771 .stroke-N1{stroke:#0A0F25;}
|
.d2-281690071 .stroke-N1{stroke:#0A0F25;}
|
||||||
.d2-508224771 .stroke-N2{stroke:#676C7E;}
|
.d2-281690071 .stroke-N2{stroke:#676C7E;}
|
||||||
.d2-508224771 .stroke-N3{stroke:#9499AB;}
|
.d2-281690071 .stroke-N3{stroke:#9499AB;}
|
||||||
.d2-508224771 .stroke-N4{stroke:#CFD2DD;}
|
.d2-281690071 .stroke-N4{stroke:#CFD2DD;}
|
||||||
.d2-508224771 .stroke-N5{stroke:#DEE1EB;}
|
.d2-281690071 .stroke-N5{stroke:#DEE1EB;}
|
||||||
.d2-508224771 .stroke-N6{stroke:#EEF1F8;}
|
.d2-281690071 .stroke-N6{stroke:#EEF1F8;}
|
||||||
.d2-508224771 .stroke-N7{stroke:#FFFFFF;}
|
.d2-281690071 .stroke-N7{stroke:#FFFFFF;}
|
||||||
.d2-508224771 .stroke-B1{stroke:#0D32B2;}
|
.d2-281690071 .stroke-B1{stroke:#0D32B2;}
|
||||||
.d2-508224771 .stroke-B2{stroke:#0D32B2;}
|
.d2-281690071 .stroke-B2{stroke:#0D32B2;}
|
||||||
.d2-508224771 .stroke-B3{stroke:#E3E9FD;}
|
.d2-281690071 .stroke-B3{stroke:#E3E9FD;}
|
||||||
.d2-508224771 .stroke-B4{stroke:#E3E9FD;}
|
.d2-281690071 .stroke-B4{stroke:#E3E9FD;}
|
||||||
.d2-508224771 .stroke-B5{stroke:#EDF0FD;}
|
.d2-281690071 .stroke-B5{stroke:#EDF0FD;}
|
||||||
.d2-508224771 .stroke-B6{stroke:#F7F8FE;}
|
.d2-281690071 .stroke-B6{stroke:#F7F8FE;}
|
||||||
.d2-508224771 .stroke-AA2{stroke:#4A6FF3;}
|
.d2-281690071 .stroke-AA2{stroke:#4A6FF3;}
|
||||||
.d2-508224771 .stroke-AA4{stroke:#EDF0FD;}
|
.d2-281690071 .stroke-AA4{stroke:#EDF0FD;}
|
||||||
.d2-508224771 .stroke-AA5{stroke:#F7F8FE;}
|
.d2-281690071 .stroke-AA5{stroke:#F7F8FE;}
|
||||||
.d2-508224771 .stroke-AB4{stroke:#EDF0FD;}
|
.d2-281690071 .stroke-AB4{stroke:#EDF0FD;}
|
||||||
.d2-508224771 .stroke-AB5{stroke:#F7F8FE;}
|
.d2-281690071 .stroke-AB5{stroke:#F7F8FE;}
|
||||||
.d2-508224771 .background-color-N1{background-color:#0A0F25;}
|
.d2-281690071 .background-color-N1{background-color:#0A0F25;}
|
||||||
.d2-508224771 .background-color-N2{background-color:#676C7E;}
|
.d2-281690071 .background-color-N2{background-color:#676C7E;}
|
||||||
.d2-508224771 .background-color-N3{background-color:#9499AB;}
|
.d2-281690071 .background-color-N3{background-color:#9499AB;}
|
||||||
.d2-508224771 .background-color-N4{background-color:#CFD2DD;}
|
.d2-281690071 .background-color-N4{background-color:#CFD2DD;}
|
||||||
.d2-508224771 .background-color-N5{background-color:#DEE1EB;}
|
.d2-281690071 .background-color-N5{background-color:#DEE1EB;}
|
||||||
.d2-508224771 .background-color-N6{background-color:#EEF1F8;}
|
.d2-281690071 .background-color-N6{background-color:#EEF1F8;}
|
||||||
.d2-508224771 .background-color-N7{background-color:#FFFFFF;}
|
.d2-281690071 .background-color-N7{background-color:#FFFFFF;}
|
||||||
.d2-508224771 .background-color-B1{background-color:#0D32B2;}
|
.d2-281690071 .background-color-B1{background-color:#0D32B2;}
|
||||||
.d2-508224771 .background-color-B2{background-color:#0D32B2;}
|
.d2-281690071 .background-color-B2{background-color:#0D32B2;}
|
||||||
.d2-508224771 .background-color-B3{background-color:#E3E9FD;}
|
.d2-281690071 .background-color-B3{background-color:#E3E9FD;}
|
||||||
.d2-508224771 .background-color-B4{background-color:#E3E9FD;}
|
.d2-281690071 .background-color-B4{background-color:#E3E9FD;}
|
||||||
.d2-508224771 .background-color-B5{background-color:#EDF0FD;}
|
.d2-281690071 .background-color-B5{background-color:#EDF0FD;}
|
||||||
.d2-508224771 .background-color-B6{background-color:#F7F8FE;}
|
.d2-281690071 .background-color-B6{background-color:#F7F8FE;}
|
||||||
.d2-508224771 .background-color-AA2{background-color:#4A6FF3;}
|
.d2-281690071 .background-color-AA2{background-color:#4A6FF3;}
|
||||||
.d2-508224771 .background-color-AA4{background-color:#EDF0FD;}
|
.d2-281690071 .background-color-AA4{background-color:#EDF0FD;}
|
||||||
.d2-508224771 .background-color-AA5{background-color:#F7F8FE;}
|
.d2-281690071 .background-color-AA5{background-color:#F7F8FE;}
|
||||||
.d2-508224771 .background-color-AB4{background-color:#EDF0FD;}
|
.d2-281690071 .background-color-AB4{background-color:#EDF0FD;}
|
||||||
.d2-508224771 .background-color-AB5{background-color:#F7F8FE;}
|
.d2-281690071 .background-color-AB5{background-color:#F7F8FE;}
|
||||||
.d2-508224771 .color-N1{color:#0A0F25;}
|
.d2-281690071 .color-N1{color:#0A0F25;}
|
||||||
.d2-508224771 .color-N2{color:#676C7E;}
|
.d2-281690071 .color-N2{color:#676C7E;}
|
||||||
.d2-508224771 .color-N3{color:#9499AB;}
|
.d2-281690071 .color-N3{color:#9499AB;}
|
||||||
.d2-508224771 .color-N4{color:#CFD2DD;}
|
.d2-281690071 .color-N4{color:#CFD2DD;}
|
||||||
.d2-508224771 .color-N5{color:#DEE1EB;}
|
.d2-281690071 .color-N5{color:#DEE1EB;}
|
||||||
.d2-508224771 .color-N6{color:#EEF1F8;}
|
.d2-281690071 .color-N6{color:#EEF1F8;}
|
||||||
.d2-508224771 .color-N7{color:#FFFFFF;}
|
.d2-281690071 .color-N7{color:#FFFFFF;}
|
||||||
.d2-508224771 .color-B1{color:#0D32B2;}
|
.d2-281690071 .color-B1{color:#0D32B2;}
|
||||||
.d2-508224771 .color-B2{color:#0D32B2;}
|
.d2-281690071 .color-B2{color:#0D32B2;}
|
||||||
.d2-508224771 .color-B3{color:#E3E9FD;}
|
.d2-281690071 .color-B3{color:#E3E9FD;}
|
||||||
.d2-508224771 .color-B4{color:#E3E9FD;}
|
.d2-281690071 .color-B4{color:#E3E9FD;}
|
||||||
.d2-508224771 .color-B5{color:#EDF0FD;}
|
.d2-281690071 .color-B5{color:#EDF0FD;}
|
||||||
.d2-508224771 .color-B6{color:#F7F8FE;}
|
.d2-281690071 .color-B6{color:#F7F8FE;}
|
||||||
.d2-508224771 .color-AA2{color:#4A6FF3;}
|
.d2-281690071 .color-AA2{color:#4A6FF3;}
|
||||||
.d2-508224771 .color-AA4{color:#EDF0FD;}
|
.d2-281690071 .color-AA4{color:#EDF0FD;}
|
||||||
.d2-508224771 .color-AA5{color:#F7F8FE;}
|
.d2-281690071 .color-AA5{color:#F7F8FE;}
|
||||||
.d2-508224771 .color-AB4{color:#EDF0FD;}
|
.d2-281690071 .color-AB4{color:#EDF0FD;}
|
||||||
.d2-508224771 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--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-bright);mix-blend-mode:darken}.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-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.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-bright);mix-blend-mode:darken}.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,
|
.d2-281690071 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--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-bright);mix-blend-mode:darken}.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-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.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-bright);mix-blend-mode:darken}.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 {
|
.md dfn {
|
||||||
font-family: "d2-508224771-font-italic";
|
font-family: "d2-281690071-font-italic";
|
||||||
}
|
}
|
||||||
|
|
||||||
.md b,
|
.md b,
|
||||||
.md strong {
|
.md strong {
|
||||||
font-family: "d2-508224771-font-bold";
|
font-family: "d2-281690071-font-bold";
|
||||||
}
|
}
|
||||||
|
|
||||||
.md code,
|
.md code,
|
||||||
.md kbd,
|
.md kbd,
|
||||||
.md pre,
|
.md pre,
|
||||||
.md samp {
|
.md samp {
|
||||||
font-family: "d2-508224771-font-mono";
|
font-family: "d2-281690071-font-mono";
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,7 +126,7 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
color: var(--color-fg-default);
|
color: var(--color-fg-default);
|
||||||
background-color: transparent; /* we don't want to define the background color */
|
background-color: transparent; /* we don't want to define the background color */
|
||||||
font-family: "d2-508224771-font-regular";
|
font-family: "d2-281690071-font-regular";
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
|
|
@ -832,7 +832,7 @@
|
||||||
.md .contains-task-list:dir(rtl) .task-list-item-checkbox {
|
.md .contains-task-list:dir(rtl) .task-list-item-checkbox {
|
||||||
margin: 0 -1.6em 0.25em 0.2em;
|
margin: 0 -1.6em 0.25em 0.2em;
|
||||||
}
|
}
|
||||||
</style><style type="text/css"><![CDATA[@keyframes d2Transition-d2-508224771-0 {
|
</style><style type="text/css"><![CDATA[@keyframes d2Transition-d2-281690071-0 {
|
||||||
0%, 0.000000% {
|
0%, 0.000000% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -842,7 +842,7 @@
|
||||||
25.000000%, 100% {
|
25.000000%, 100% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}@keyframes d2Transition-d2-508224771-1 {
|
}@keyframes d2Transition-d2-281690071-1 {
|
||||||
0%, 24.982143% {
|
0%, 24.982143% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -852,7 +852,7 @@
|
||||||
50.000000%, 100% {
|
50.000000%, 100% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}@keyframes d2Transition-d2-508224771-2 {
|
}@keyframes d2Transition-d2-281690071-2 {
|
||||||
0%, 49.982143% {
|
0%, 49.982143% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
@ -862,26 +862,26 @@
|
||||||
75.000000%, 100% {
|
75.000000%, 100% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
}@keyframes d2Transition-d2-508224771-3 {
|
}@keyframes d2Transition-d2-281690071-3 {
|
||||||
0%, 74.982143% {
|
0%, 74.982143% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
75.000000%, 100.000000% {
|
75.000000%, 100.000000% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
}]]></style><g style="animation: d2Transition-d2-508224771-0 5600ms infinite" class="d2-508224771" width="412" height="247" viewBox="-206 -166 412 247"><rect x="-206.000000" y="-166.000000" width="412.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 fill-N1" style="text-anchor:middle;font-size:35px">Chicken's plan</text></g><mask id="d2-508224771" maskUnits="userSpaceOnUse" x="-206" y="-166" width="412" height="247">
|
}]]></style><g style="animation: d2Transition-d2-281690071-0 5600ms infinite" class="d2-281690071" width="412" height="247" viewBox="-206 -166 412 247"><rect x="-206.000000" y="-166.000000" width="412.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 fill-N1" style="text-anchor:middle;font-size:35px">Chicken's plan</text></g><mask id="d2-281690071" maskUnits="userSpaceOnUse" x="-206" y="-166" width="412" height="247">
|
||||||
<rect x="-206" y="-166" width="412" height="247" fill="white"></rect>
|
<rect x="-206" y="-166" width="412" height="247" fill="white"></rect>
|
||||||
<rect x="-105.000000" y="-65.000000" width="210" height="45" fill="rgba(0,0,0,0.75)"></rect>
|
<rect x="-105.000000" y="-65.000000" width="210" height="45" fill="rgba(0,0,0,0.75)"></rect>
|
||||||
</mask></g><g style="animation: d2Transition-d2-508224771-1 5600ms infinite" class="d2-508224771" width="412" height="333" viewBox="-131 -166 412 333"><rect x="-131.000000" y="-166.000000" width="412.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="150.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="75.000000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Approach road</text></g><g id=""Chicken's plan""><g class="shape" ></g><text x="75.000000" y="-30.000000" class="text fill-N1" style="text-anchor:middle;font-size:35px">Chicken's plan</text></g><mask id="d2-3302893893" maskUnits="userSpaceOnUse" x="-131" y="-166" width="412" height="333">
|
</mask></g><g style="animation: d2Transition-d2-281690071-1 5600ms infinite" class="d2-281690071" width="412" height="333" viewBox="-131 -166 412 333"><rect x="-131.000000" y="-166.000000" width="412.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="150.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="75.000000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Approach road</text></g><g id=""Chicken's plan""><g class="shape" ></g><text x="75.000000" y="-30.000000" class="text fill-N1" style="text-anchor:middle;font-size:35px">Chicken's plan</text></g><mask id="d2-2457953887" maskUnits="userSpaceOnUse" x="-131" y="-166" width="412" height="333">
|
||||||
<rect x="-131" y="-166" width="412" height="333" fill="white"></rect>
|
<rect x="-131" y="-166" width="412" height="333" fill="white"></rect>
|
||||||
<rect x="22.500000" y="22.500000" width="105" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
<rect x="22.500000" y="22.500000" width="105" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||||
<rect x="-30.000000" y="-65.000000" width="210" height="45" fill="rgba(0,0,0,0.75)"></rect>
|
<rect x="-30.000000" y="-65.000000" width="210" height="45" fill="rgba(0,0,0,0.75)"></rect>
|
||||||
</mask></g><g style="animation: d2Transition-d2-508224771-2 5600ms infinite" class="d2-508224771" width="412" height="499" viewBox="-131 -166 412 499"><rect x="-131.000000" y="-166.000000" width="412.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="150.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="75.000000" y="38.500000" class="text-bold 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="120.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="75.000000" y="204.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Cross road</text></g><g id=""Chicken's plan""><g class="shape" ></g><text x="75.000000" y="-30.000000" class="text 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 75.000000 68.000000 C 75.000000 106.000000 75.000000 126.000000 75.000000 162.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-2454480105)" /></g><mask id="d2-2454480105" maskUnits="userSpaceOnUse" x="-131" y="-166" width="412" height="499">
|
</mask></g><g style="animation: d2Transition-d2-281690071-2 5600ms infinite" class="d2-281690071" width="412" height="499" viewBox="-131 -166 412 499"><rect x="-131.000000" y="-166.000000" width="412.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="150.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="75.000000" y="38.500000" class="text-bold 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="120.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="75.000000" y="204.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Cross road</text></g><g id=""Chicken's plan""><g class="shape" ></g><text x="75.000000" y="-30.000000" class="text 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 75.000000 68.000000 C 75.000000 106.000000 75.000000 126.000000 75.000000 162.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-1673714443)" /></g><mask id="d2-1673714443" maskUnits="userSpaceOnUse" x="-131" y="-166" width="412" height="499">
|
||||||
<rect x="-131" y="-166" width="412" height="499" fill="white"></rect>
|
<rect x="-131" y="-166" width="412" height="499" fill="white"></rect>
|
||||||
<rect x="22.500000" y="22.500000" width="105" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
<rect x="22.500000" y="22.500000" width="105" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||||
<rect x="37.500000" y="188.500000" width="75" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
<rect x="37.500000" y="188.500000" width="75" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||||
<rect x="-30.000000" y="-65.000000" width="210" height="45" fill="rgba(0,0,0,0.75)"></rect>
|
<rect x="-30.000000" y="-65.000000" width="210" height="45" fill="rgba(0,0,0,0.75)"></rect>
|
||||||
</mask></g><g style="animation: d2Transition-d2-508224771-3 5600ms infinite" class="d2-508224771" width="412" height="665" viewBox="-104 -166 412 665"><rect x="-104.000000" y="-166.000000" width="412.000000" height="665.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><g id="Approach road"><g class="shape" ><rect x="27.000000" y="0.000000" width="150.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="102.000000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Approach road</text></g><g id="Cross road"><g class="shape" ><rect x="42.000000" y="166.000000" width="120.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="102.000000" y="204.500000" class="text-bold 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="203.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="101.500000" y="370.500000" class="text-bold 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="102.000000" y="-30.000000" class="text 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 101.500000 68.000000 C 101.500000 106.000000 101.500000 126.000000 101.500000 162.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-682060979)" /></g><g id="(Cross road -> Make you wonder why)[0]"><path d="M 101.500000 234.000000 C 101.500000 272.000000 101.500000 292.000000 101.500000 328.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-682060979)" /></g><mask id="d2-682060979" maskUnits="userSpaceOnUse" x="-104" y="-166" width="412" height="665">
|
</mask></g><g style="animation: d2Transition-d2-281690071-3 5600ms infinite" class="d2-281690071" width="412" height="665" viewBox="-104 -166 412 665"><rect x="-104.000000" y="-166.000000" width="412.000000" height="665.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><g id="Approach road"><g class="shape" ><rect x="27.000000" y="0.000000" width="150.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="102.000000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Approach road</text></g><g id="Cross road"><g class="shape" ><rect x="42.000000" y="166.000000" width="120.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="102.000000" y="204.500000" class="text-bold 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="203.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="101.500000" y="370.500000" class="text-bold 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="102.000000" y="-30.000000" class="text 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 101.500000 68.000000 C 101.500000 106.000000 101.500000 126.000000 101.500000 162.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-1425945673)" /></g><g id="(Cross road -> Make you wonder why)[0]"><path d="M 101.500000 234.000000 C 101.500000 272.000000 101.500000 292.000000 101.500000 328.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-end="url(#mk-3488378134)" mask="url(#d2-1425945673)" /></g><mask id="d2-1425945673" maskUnits="userSpaceOnUse" x="-104" y="-166" width="412" height="665">
|
||||||
<rect x="-104" y="-166" width="412" height="665" fill="white"></rect>
|
<rect x="-104" y="-166" width="412" height="665" fill="white"></rect>
|
||||||
<rect x="49.500000" y="22.500000" width="105" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
<rect x="49.500000" y="22.500000" width="105" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||||
<rect x="64.500000" y="188.500000" width="75" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
<rect x="64.500000" y="188.500000" width="75" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
|
@ -1,9 +1,9 @@
|
||||||
<?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 290 268"><svg id="d2-svg" class="d2-685498927" width="290" height="268" viewBox="-101 -101 290 268"><rect x="-101.000000" y="-101.000000" width="290.000000" height="268.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
<?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.6.0-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 290 268"><svg id="d2-svg" class="d2-3054270525" width="290" height="268" viewBox="-101 -101 290 268"><rect x="-101.000000" y="-101.000000" width="290.000000" height="268.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||||
.d2-685498927 .text-bold {
|
.d2-3054270525 .text-bold {
|
||||||
font-family: "d2-685498927-font-bold";
|
font-family: "d2-3054270525-font-bold";
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: d2-685498927-font-bold;
|
font-family: d2-3054270525-font-bold;
|
||||||
src: url("data:application/font-woff;base64,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");
|
src: url("data:application/font-woff;base64,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");
|
||||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||||
shape-rendering: geometricPrecision;
|
shape-rendering: geometricPrecision;
|
||||||
|
|
@ -18,78 +18,78 @@
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2-685498927 .fill-N1{fill:#0A0F25;}
|
.d2-3054270525 .fill-N1{fill:#0A0F25;}
|
||||||
.d2-685498927 .fill-N2{fill:#676C7E;}
|
.d2-3054270525 .fill-N2{fill:#676C7E;}
|
||||||
.d2-685498927 .fill-N3{fill:#9499AB;}
|
.d2-3054270525 .fill-N3{fill:#9499AB;}
|
||||||
.d2-685498927 .fill-N4{fill:#CFD2DD;}
|
.d2-3054270525 .fill-N4{fill:#CFD2DD;}
|
||||||
.d2-685498927 .fill-N5{fill:#DEE1EB;}
|
.d2-3054270525 .fill-N5{fill:#DEE1EB;}
|
||||||
.d2-685498927 .fill-N6{fill:#EEF1F8;}
|
.d2-3054270525 .fill-N6{fill:#EEF1F8;}
|
||||||
.d2-685498927 .fill-N7{fill:#FFFFFF;}
|
.d2-3054270525 .fill-N7{fill:#FFFFFF;}
|
||||||
.d2-685498927 .fill-B1{fill:#0D32B2;}
|
.d2-3054270525 .fill-B1{fill:#0D32B2;}
|
||||||
.d2-685498927 .fill-B2{fill:#0D32B2;}
|
.d2-3054270525 .fill-B2{fill:#0D32B2;}
|
||||||
.d2-685498927 .fill-B3{fill:#E3E9FD;}
|
.d2-3054270525 .fill-B3{fill:#E3E9FD;}
|
||||||
.d2-685498927 .fill-B4{fill:#E3E9FD;}
|
.d2-3054270525 .fill-B4{fill:#E3E9FD;}
|
||||||
.d2-685498927 .fill-B5{fill:#EDF0FD;}
|
.d2-3054270525 .fill-B5{fill:#EDF0FD;}
|
||||||
.d2-685498927 .fill-B6{fill:#F7F8FE;}
|
.d2-3054270525 .fill-B6{fill:#F7F8FE;}
|
||||||
.d2-685498927 .fill-AA2{fill:#4A6FF3;}
|
.d2-3054270525 .fill-AA2{fill:#4A6FF3;}
|
||||||
.d2-685498927 .fill-AA4{fill:#EDF0FD;}
|
.d2-3054270525 .fill-AA4{fill:#EDF0FD;}
|
||||||
.d2-685498927 .fill-AA5{fill:#F7F8FE;}
|
.d2-3054270525 .fill-AA5{fill:#F7F8FE;}
|
||||||
.d2-685498927 .fill-AB4{fill:#EDF0FD;}
|
.d2-3054270525 .fill-AB4{fill:#EDF0FD;}
|
||||||
.d2-685498927 .fill-AB5{fill:#F7F8FE;}
|
.d2-3054270525 .fill-AB5{fill:#F7F8FE;}
|
||||||
.d2-685498927 .stroke-N1{stroke:#0A0F25;}
|
.d2-3054270525 .stroke-N1{stroke:#0A0F25;}
|
||||||
.d2-685498927 .stroke-N2{stroke:#676C7E;}
|
.d2-3054270525 .stroke-N2{stroke:#676C7E;}
|
||||||
.d2-685498927 .stroke-N3{stroke:#9499AB;}
|
.d2-3054270525 .stroke-N3{stroke:#9499AB;}
|
||||||
.d2-685498927 .stroke-N4{stroke:#CFD2DD;}
|
.d2-3054270525 .stroke-N4{stroke:#CFD2DD;}
|
||||||
.d2-685498927 .stroke-N5{stroke:#DEE1EB;}
|
.d2-3054270525 .stroke-N5{stroke:#DEE1EB;}
|
||||||
.d2-685498927 .stroke-N6{stroke:#EEF1F8;}
|
.d2-3054270525 .stroke-N6{stroke:#EEF1F8;}
|
||||||
.d2-685498927 .stroke-N7{stroke:#FFFFFF;}
|
.d2-3054270525 .stroke-N7{stroke:#FFFFFF;}
|
||||||
.d2-685498927 .stroke-B1{stroke:#0D32B2;}
|
.d2-3054270525 .stroke-B1{stroke:#0D32B2;}
|
||||||
.d2-685498927 .stroke-B2{stroke:#0D32B2;}
|
.d2-3054270525 .stroke-B2{stroke:#0D32B2;}
|
||||||
.d2-685498927 .stroke-B3{stroke:#E3E9FD;}
|
.d2-3054270525 .stroke-B3{stroke:#E3E9FD;}
|
||||||
.d2-685498927 .stroke-B4{stroke:#E3E9FD;}
|
.d2-3054270525 .stroke-B4{stroke:#E3E9FD;}
|
||||||
.d2-685498927 .stroke-B5{stroke:#EDF0FD;}
|
.d2-3054270525 .stroke-B5{stroke:#EDF0FD;}
|
||||||
.d2-685498927 .stroke-B6{stroke:#F7F8FE;}
|
.d2-3054270525 .stroke-B6{stroke:#F7F8FE;}
|
||||||
.d2-685498927 .stroke-AA2{stroke:#4A6FF3;}
|
.d2-3054270525 .stroke-AA2{stroke:#4A6FF3;}
|
||||||
.d2-685498927 .stroke-AA4{stroke:#EDF0FD;}
|
.d2-3054270525 .stroke-AA4{stroke:#EDF0FD;}
|
||||||
.d2-685498927 .stroke-AA5{stroke:#F7F8FE;}
|
.d2-3054270525 .stroke-AA5{stroke:#F7F8FE;}
|
||||||
.d2-685498927 .stroke-AB4{stroke:#EDF0FD;}
|
.d2-3054270525 .stroke-AB4{stroke:#EDF0FD;}
|
||||||
.d2-685498927 .stroke-AB5{stroke:#F7F8FE;}
|
.d2-3054270525 .stroke-AB5{stroke:#F7F8FE;}
|
||||||
.d2-685498927 .background-color-N1{background-color:#0A0F25;}
|
.d2-3054270525 .background-color-N1{background-color:#0A0F25;}
|
||||||
.d2-685498927 .background-color-N2{background-color:#676C7E;}
|
.d2-3054270525 .background-color-N2{background-color:#676C7E;}
|
||||||
.d2-685498927 .background-color-N3{background-color:#9499AB;}
|
.d2-3054270525 .background-color-N3{background-color:#9499AB;}
|
||||||
.d2-685498927 .background-color-N4{background-color:#CFD2DD;}
|
.d2-3054270525 .background-color-N4{background-color:#CFD2DD;}
|
||||||
.d2-685498927 .background-color-N5{background-color:#DEE1EB;}
|
.d2-3054270525 .background-color-N5{background-color:#DEE1EB;}
|
||||||
.d2-685498927 .background-color-N6{background-color:#EEF1F8;}
|
.d2-3054270525 .background-color-N6{background-color:#EEF1F8;}
|
||||||
.d2-685498927 .background-color-N7{background-color:#FFFFFF;}
|
.d2-3054270525 .background-color-N7{background-color:#FFFFFF;}
|
||||||
.d2-685498927 .background-color-B1{background-color:#0D32B2;}
|
.d2-3054270525 .background-color-B1{background-color:#0D32B2;}
|
||||||
.d2-685498927 .background-color-B2{background-color:#0D32B2;}
|
.d2-3054270525 .background-color-B2{background-color:#0D32B2;}
|
||||||
.d2-685498927 .background-color-B3{background-color:#E3E9FD;}
|
.d2-3054270525 .background-color-B3{background-color:#E3E9FD;}
|
||||||
.d2-685498927 .background-color-B4{background-color:#E3E9FD;}
|
.d2-3054270525 .background-color-B4{background-color:#E3E9FD;}
|
||||||
.d2-685498927 .background-color-B5{background-color:#EDF0FD;}
|
.d2-3054270525 .background-color-B5{background-color:#EDF0FD;}
|
||||||
.d2-685498927 .background-color-B6{background-color:#F7F8FE;}
|
.d2-3054270525 .background-color-B6{background-color:#F7F8FE;}
|
||||||
.d2-685498927 .background-color-AA2{background-color:#4A6FF3;}
|
.d2-3054270525 .background-color-AA2{background-color:#4A6FF3;}
|
||||||
.d2-685498927 .background-color-AA4{background-color:#EDF0FD;}
|
.d2-3054270525 .background-color-AA4{background-color:#EDF0FD;}
|
||||||
.d2-685498927 .background-color-AA5{background-color:#F7F8FE;}
|
.d2-3054270525 .background-color-AA5{background-color:#F7F8FE;}
|
||||||
.d2-685498927 .background-color-AB4{background-color:#EDF0FD;}
|
.d2-3054270525 .background-color-AB4{background-color:#EDF0FD;}
|
||||||
.d2-685498927 .background-color-AB5{background-color:#F7F8FE;}
|
.d2-3054270525 .background-color-AB5{background-color:#F7F8FE;}
|
||||||
.d2-685498927 .color-N1{color:#0A0F25;}
|
.d2-3054270525 .color-N1{color:#0A0F25;}
|
||||||
.d2-685498927 .color-N2{color:#676C7E;}
|
.d2-3054270525 .color-N2{color:#676C7E;}
|
||||||
.d2-685498927 .color-N3{color:#9499AB;}
|
.d2-3054270525 .color-N3{color:#9499AB;}
|
||||||
.d2-685498927 .color-N4{color:#CFD2DD;}
|
.d2-3054270525 .color-N4{color:#CFD2DD;}
|
||||||
.d2-685498927 .color-N5{color:#DEE1EB;}
|
.d2-3054270525 .color-N5{color:#DEE1EB;}
|
||||||
.d2-685498927 .color-N6{color:#EEF1F8;}
|
.d2-3054270525 .color-N6{color:#EEF1F8;}
|
||||||
.d2-685498927 .color-N7{color:#FFFFFF;}
|
.d2-3054270525 .color-N7{color:#FFFFFF;}
|
||||||
.d2-685498927 .color-B1{color:#0D32B2;}
|
.d2-3054270525 .color-B1{color:#0D32B2;}
|
||||||
.d2-685498927 .color-B2{color:#0D32B2;}
|
.d2-3054270525 .color-B2{color:#0D32B2;}
|
||||||
.d2-685498927 .color-B3{color:#E3E9FD;}
|
.d2-3054270525 .color-B3{color:#E3E9FD;}
|
||||||
.d2-685498927 .color-B4{color:#E3E9FD;}
|
.d2-3054270525 .color-B4{color:#E3E9FD;}
|
||||||
.d2-685498927 .color-B5{color:#EDF0FD;}
|
.d2-3054270525 .color-B5{color:#EDF0FD;}
|
||||||
.d2-685498927 .color-B6{color:#F7F8FE;}
|
.d2-3054270525 .color-B6{color:#F7F8FE;}
|
||||||
.d2-685498927 .color-AA2{color:#4A6FF3;}
|
.d2-3054270525 .color-AA2{color:#4A6FF3;}
|
||||||
.d2-685498927 .color-AA4{color:#EDF0FD;}
|
.d2-3054270525 .color-AA4{color:#EDF0FD;}
|
||||||
.d2-685498927 .color-AA5{color:#F7F8FE;}
|
.d2-3054270525 .color-AA5{color:#F7F8FE;}
|
||||||
.d2-685498927 .color-AB4{color:#EDF0FD;}
|
.d2-3054270525 .color-AB4{color:#EDF0FD;}
|
||||||
.d2-685498927 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--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-bright);mix-blend-mode:darken}.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-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.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-bright);mix-blend-mode:darken}.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><g id="meow"><g class="shape" ><rect x="0.000000" y="0.000000" width="88.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="44.000000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">meow</text></g><mask id="d2-685498927" maskUnits="userSpaceOnUse" x="-101" y="-101" width="290" height="268">
|
.d2-3054270525 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--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-bright);mix-blend-mode:darken}.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-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.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-bright);mix-blend-mode:darken}.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><g id="meow"><g class="shape" ><rect x="0.000000" y="0.000000" width="88.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="44.000000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">meow</text></g><mask id="d2-3054270525" maskUnits="userSpaceOnUse" x="-101" y="-101" width="290" height="268">
|
||||||
<rect x="-101" y="-101" width="290" height="268" fill="white"></rect>
|
<rect x="-101" y="-101" width="290" height="268" fill="white"></rect>
|
||||||
<rect x="22.500000" y="22.500000" width="43" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
<rect x="22.500000" y="22.500000" width="43" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||||
</mask></svg></svg>
|
</mask></svg></svg>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 8.9 KiB |
|
|
@ -1,12 +1,12 @@
|
||||||
<?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 305 285"><svg id="d2-svg" class="d2-2353227294" width="305" height="285" viewBox="-101 -118 305 285"><rect x="-101.000000" y="-118.000000" width="305.000000" height="285.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
<?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.6.0-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 305 285"><svg id="d2-svg" class="d2-1655546234" width="305" height="285" viewBox="-101 -118 305 285"><rect x="-101.000000" y="-118.000000" width="305.000000" height="285.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||||
.appendix-icon {
|
.appendix-icon {
|
||||||
filter: drop-shadow(0px 0px 32px rgba(31, 36, 58, 0.1));
|
filter: drop-shadow(0px 0px 32px rgba(31, 36, 58, 0.1));
|
||||||
}
|
}
|
||||||
.d2-2353227294 .text-bold {
|
.d2-1655546234 .text-bold {
|
||||||
font-family: "d2-2353227294-font-bold";
|
font-family: "d2-1655546234-font-bold";
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: d2-2353227294-font-bold;
|
font-family: d2-1655546234-font-bold;
|
||||||
src: url("data:application/font-woff;base64,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");
|
src: url("data:application/font-woff;base64,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");
|
||||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||||
shape-rendering: geometricPrecision;
|
shape-rendering: geometricPrecision;
|
||||||
|
|
@ -21,78 +21,78 @@
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2-2353227294 .fill-N1{fill:#0A0F25;}
|
.d2-1655546234 .fill-N1{fill:#0A0F25;}
|
||||||
.d2-2353227294 .fill-N2{fill:#676C7E;}
|
.d2-1655546234 .fill-N2{fill:#676C7E;}
|
||||||
.d2-2353227294 .fill-N3{fill:#9499AB;}
|
.d2-1655546234 .fill-N3{fill:#9499AB;}
|
||||||
.d2-2353227294 .fill-N4{fill:#CFD2DD;}
|
.d2-1655546234 .fill-N4{fill:#CFD2DD;}
|
||||||
.d2-2353227294 .fill-N5{fill:#DEE1EB;}
|
.d2-1655546234 .fill-N5{fill:#DEE1EB;}
|
||||||
.d2-2353227294 .fill-N6{fill:#EEF1F8;}
|
.d2-1655546234 .fill-N6{fill:#EEF1F8;}
|
||||||
.d2-2353227294 .fill-N7{fill:#FFFFFF;}
|
.d2-1655546234 .fill-N7{fill:#FFFFFF;}
|
||||||
.d2-2353227294 .fill-B1{fill:#0D32B2;}
|
.d2-1655546234 .fill-B1{fill:#0D32B2;}
|
||||||
.d2-2353227294 .fill-B2{fill:#0D32B2;}
|
.d2-1655546234 .fill-B2{fill:#0D32B2;}
|
||||||
.d2-2353227294 .fill-B3{fill:#E3E9FD;}
|
.d2-1655546234 .fill-B3{fill:#E3E9FD;}
|
||||||
.d2-2353227294 .fill-B4{fill:#E3E9FD;}
|
.d2-1655546234 .fill-B4{fill:#E3E9FD;}
|
||||||
.d2-2353227294 .fill-B5{fill:#EDF0FD;}
|
.d2-1655546234 .fill-B5{fill:#EDF0FD;}
|
||||||
.d2-2353227294 .fill-B6{fill:#F7F8FE;}
|
.d2-1655546234 .fill-B6{fill:#F7F8FE;}
|
||||||
.d2-2353227294 .fill-AA2{fill:#4A6FF3;}
|
.d2-1655546234 .fill-AA2{fill:#4A6FF3;}
|
||||||
.d2-2353227294 .fill-AA4{fill:#EDF0FD;}
|
.d2-1655546234 .fill-AA4{fill:#EDF0FD;}
|
||||||
.d2-2353227294 .fill-AA5{fill:#F7F8FE;}
|
.d2-1655546234 .fill-AA5{fill:#F7F8FE;}
|
||||||
.d2-2353227294 .fill-AB4{fill:#EDF0FD;}
|
.d2-1655546234 .fill-AB4{fill:#EDF0FD;}
|
||||||
.d2-2353227294 .fill-AB5{fill:#F7F8FE;}
|
.d2-1655546234 .fill-AB5{fill:#F7F8FE;}
|
||||||
.d2-2353227294 .stroke-N1{stroke:#0A0F25;}
|
.d2-1655546234 .stroke-N1{stroke:#0A0F25;}
|
||||||
.d2-2353227294 .stroke-N2{stroke:#676C7E;}
|
.d2-1655546234 .stroke-N2{stroke:#676C7E;}
|
||||||
.d2-2353227294 .stroke-N3{stroke:#9499AB;}
|
.d2-1655546234 .stroke-N3{stroke:#9499AB;}
|
||||||
.d2-2353227294 .stroke-N4{stroke:#CFD2DD;}
|
.d2-1655546234 .stroke-N4{stroke:#CFD2DD;}
|
||||||
.d2-2353227294 .stroke-N5{stroke:#DEE1EB;}
|
.d2-1655546234 .stroke-N5{stroke:#DEE1EB;}
|
||||||
.d2-2353227294 .stroke-N6{stroke:#EEF1F8;}
|
.d2-1655546234 .stroke-N6{stroke:#EEF1F8;}
|
||||||
.d2-2353227294 .stroke-N7{stroke:#FFFFFF;}
|
.d2-1655546234 .stroke-N7{stroke:#FFFFFF;}
|
||||||
.d2-2353227294 .stroke-B1{stroke:#0D32B2;}
|
.d2-1655546234 .stroke-B1{stroke:#0D32B2;}
|
||||||
.d2-2353227294 .stroke-B2{stroke:#0D32B2;}
|
.d2-1655546234 .stroke-B2{stroke:#0D32B2;}
|
||||||
.d2-2353227294 .stroke-B3{stroke:#E3E9FD;}
|
.d2-1655546234 .stroke-B3{stroke:#E3E9FD;}
|
||||||
.d2-2353227294 .stroke-B4{stroke:#E3E9FD;}
|
.d2-1655546234 .stroke-B4{stroke:#E3E9FD;}
|
||||||
.d2-2353227294 .stroke-B5{stroke:#EDF0FD;}
|
.d2-1655546234 .stroke-B5{stroke:#EDF0FD;}
|
||||||
.d2-2353227294 .stroke-B6{stroke:#F7F8FE;}
|
.d2-1655546234 .stroke-B6{stroke:#F7F8FE;}
|
||||||
.d2-2353227294 .stroke-AA2{stroke:#4A6FF3;}
|
.d2-1655546234 .stroke-AA2{stroke:#4A6FF3;}
|
||||||
.d2-2353227294 .stroke-AA4{stroke:#EDF0FD;}
|
.d2-1655546234 .stroke-AA4{stroke:#EDF0FD;}
|
||||||
.d2-2353227294 .stroke-AA5{stroke:#F7F8FE;}
|
.d2-1655546234 .stroke-AA5{stroke:#F7F8FE;}
|
||||||
.d2-2353227294 .stroke-AB4{stroke:#EDF0FD;}
|
.d2-1655546234 .stroke-AB4{stroke:#EDF0FD;}
|
||||||
.d2-2353227294 .stroke-AB5{stroke:#F7F8FE;}
|
.d2-1655546234 .stroke-AB5{stroke:#F7F8FE;}
|
||||||
.d2-2353227294 .background-color-N1{background-color:#0A0F25;}
|
.d2-1655546234 .background-color-N1{background-color:#0A0F25;}
|
||||||
.d2-2353227294 .background-color-N2{background-color:#676C7E;}
|
.d2-1655546234 .background-color-N2{background-color:#676C7E;}
|
||||||
.d2-2353227294 .background-color-N3{background-color:#9499AB;}
|
.d2-1655546234 .background-color-N3{background-color:#9499AB;}
|
||||||
.d2-2353227294 .background-color-N4{background-color:#CFD2DD;}
|
.d2-1655546234 .background-color-N4{background-color:#CFD2DD;}
|
||||||
.d2-2353227294 .background-color-N5{background-color:#DEE1EB;}
|
.d2-1655546234 .background-color-N5{background-color:#DEE1EB;}
|
||||||
.d2-2353227294 .background-color-N6{background-color:#EEF1F8;}
|
.d2-1655546234 .background-color-N6{background-color:#EEF1F8;}
|
||||||
.d2-2353227294 .background-color-N7{background-color:#FFFFFF;}
|
.d2-1655546234 .background-color-N7{background-color:#FFFFFF;}
|
||||||
.d2-2353227294 .background-color-B1{background-color:#0D32B2;}
|
.d2-1655546234 .background-color-B1{background-color:#0D32B2;}
|
||||||
.d2-2353227294 .background-color-B2{background-color:#0D32B2;}
|
.d2-1655546234 .background-color-B2{background-color:#0D32B2;}
|
||||||
.d2-2353227294 .background-color-B3{background-color:#E3E9FD;}
|
.d2-1655546234 .background-color-B3{background-color:#E3E9FD;}
|
||||||
.d2-2353227294 .background-color-B4{background-color:#E3E9FD;}
|
.d2-1655546234 .background-color-B4{background-color:#E3E9FD;}
|
||||||
.d2-2353227294 .background-color-B5{background-color:#EDF0FD;}
|
.d2-1655546234 .background-color-B5{background-color:#EDF0FD;}
|
||||||
.d2-2353227294 .background-color-B6{background-color:#F7F8FE;}
|
.d2-1655546234 .background-color-B6{background-color:#F7F8FE;}
|
||||||
.d2-2353227294 .background-color-AA2{background-color:#4A6FF3;}
|
.d2-1655546234 .background-color-AA2{background-color:#4A6FF3;}
|
||||||
.d2-2353227294 .background-color-AA4{background-color:#EDF0FD;}
|
.d2-1655546234 .background-color-AA4{background-color:#EDF0FD;}
|
||||||
.d2-2353227294 .background-color-AA5{background-color:#F7F8FE;}
|
.d2-1655546234 .background-color-AA5{background-color:#F7F8FE;}
|
||||||
.d2-2353227294 .background-color-AB4{background-color:#EDF0FD;}
|
.d2-1655546234 .background-color-AB4{background-color:#EDF0FD;}
|
||||||
.d2-2353227294 .background-color-AB5{background-color:#F7F8FE;}
|
.d2-1655546234 .background-color-AB5{background-color:#F7F8FE;}
|
||||||
.d2-2353227294 .color-N1{color:#0A0F25;}
|
.d2-1655546234 .color-N1{color:#0A0F25;}
|
||||||
.d2-2353227294 .color-N2{color:#676C7E;}
|
.d2-1655546234 .color-N2{color:#676C7E;}
|
||||||
.d2-2353227294 .color-N3{color:#9499AB;}
|
.d2-1655546234 .color-N3{color:#9499AB;}
|
||||||
.d2-2353227294 .color-N4{color:#CFD2DD;}
|
.d2-1655546234 .color-N4{color:#CFD2DD;}
|
||||||
.d2-2353227294 .color-N5{color:#DEE1EB;}
|
.d2-1655546234 .color-N5{color:#DEE1EB;}
|
||||||
.d2-2353227294 .color-N6{color:#EEF1F8;}
|
.d2-1655546234 .color-N6{color:#EEF1F8;}
|
||||||
.d2-2353227294 .color-N7{color:#FFFFFF;}
|
.d2-1655546234 .color-N7{color:#FFFFFF;}
|
||||||
.d2-2353227294 .color-B1{color:#0D32B2;}
|
.d2-1655546234 .color-B1{color:#0D32B2;}
|
||||||
.d2-2353227294 .color-B2{color:#0D32B2;}
|
.d2-1655546234 .color-B2{color:#0D32B2;}
|
||||||
.d2-2353227294 .color-B3{color:#E3E9FD;}
|
.d2-1655546234 .color-B3{color:#E3E9FD;}
|
||||||
.d2-2353227294 .color-B4{color:#E3E9FD;}
|
.d2-1655546234 .color-B4{color:#E3E9FD;}
|
||||||
.d2-2353227294 .color-B5{color:#EDF0FD;}
|
.d2-1655546234 .color-B5{color:#EDF0FD;}
|
||||||
.d2-2353227294 .color-B6{color:#F7F8FE;}
|
.d2-1655546234 .color-B6{color:#F7F8FE;}
|
||||||
.d2-2353227294 .color-AA2{color:#4A6FF3;}
|
.d2-1655546234 .color-AA2{color:#4A6FF3;}
|
||||||
.d2-2353227294 .color-AA4{color:#EDF0FD;}
|
.d2-1655546234 .color-AA4{color:#EDF0FD;}
|
||||||
.d2-2353227294 .color-AA5{color:#F7F8FE;}
|
.d2-1655546234 .color-AA5{color:#F7F8FE;}
|
||||||
.d2-2353227294 .color-AB4{color:#EDF0FD;}
|
.d2-1655546234 .color-AB4{color:#EDF0FD;}
|
||||||
.d2-2353227294 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--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-bright);mix-blend-mode:darken}.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-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.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-bright);mix-blend-mode:darken}.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><a href="y.svg" xlink:href="y.svg"><g id="y"><g class="shape" ><rect x="0.000000" y="0.000000" width="86.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="43.000000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">y</text></g></a><g transform="translate(70 -16)" class="appendix-icon"><svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
.d2-1655546234 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--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-bright);mix-blend-mode:darken}.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-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.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-bright);mix-blend-mode:darken}.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><a href="y.svg" xlink:href="y.svg"><g id="y"><g class="shape" ><rect x="0.000000" y="0.000000" width="86.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="43.000000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">y</text></g></a><g transform="translate(70 -16)" class="appendix-icon"><svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g clip-path="url(#clip0_3440_35088111)">
|
<g clip-path="url(#clip0_3440_35088111)">
|
||||||
<path d="M16 31.1109C24.3456 31.1109 31.1111 24.3454 31.1111 15.9998C31.1111 7.65415 24.3456 0.888672 16 0.888672C7.65436 0.888672 0.888885 7.65415 0.888885 15.9998C0.888885 24.3454 7.65436 31.1109 16 31.1109Z" fill="white" stroke="#DEE1EB"/>
|
<path d="M16 31.1109C24.3456 31.1109 31.1111 24.3454 31.1111 15.9998C31.1111 7.65415 24.3456 0.888672 16 0.888672C7.65436 0.888672 0.888885 7.65415 0.888885 15.9998C0.888885 24.3454 7.65436 31.1109 16 31.1109Z" fill="white" stroke="#DEE1EB"/>
|
||||||
<path d="M14.3909 16.7965C14.7364 17.2584 15.1772 17.6406 15.6834 17.9171C16.1896 18.1938 16.7494 18.3582 17.3248 18.3993C17.9001 18.4405 18.4777 18.3575 19.0181 18.1559C19.5586 17.9543 20.0492 17.6389 20.4571 17.2309L22.8708 14.8173C23.6036 14.0586 24.0089 13.0425 23.9998 11.9877C23.9906 10.933 23.5676 9.92404 22.8217 9.17821C22.0759 8.43237 21.067 8.00931 20.0123 8.00015C18.9575 7.99098 17.9413 8.39644 17.1827 9.1292L15.7988 10.505" stroke="#2E3346" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M14.3909 16.7965C14.7364 17.2584 15.1772 17.6406 15.6834 17.9171C16.1896 18.1938 16.7494 18.3582 17.3248 18.3993C17.9001 18.4405 18.4777 18.3575 19.0181 18.1559C19.5586 17.9543 20.0492 17.6389 20.4571 17.2309L22.8708 14.8173C23.6036 14.0586 24.0089 13.0425 23.9998 11.9877C23.9906 10.933 23.5676 9.92404 22.8217 9.17821C22.0759 8.43237 21.067 8.00931 20.0123 8.00015C18.9575 7.99098 17.9413 8.39644 17.1827 9.1292L15.7988 10.505" stroke="#2E3346" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
|
@ -104,7 +104,7 @@
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
</g><mask id="d2-2353227294" maskUnits="userSpaceOnUse" x="-101" y="-118" width="305" height="285">
|
</g><mask id="d2-1655546234" maskUnits="userSpaceOnUse" x="-101" y="-118" width="305" height="285">
|
||||||
<rect x="-101" y="-118" width="305" height="285" fill="white"></rect>
|
<rect x="-101" y="-118" width="305" height="285" fill="white"></rect>
|
||||||
<rect x="38.500000" y="22.500000" width="9" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
<rect x="38.500000" y="22.500000" width="9" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||||
</mask></svg></svg>
|
</mask></svg></svg>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
|
@ -1,12 +1,12 @@
|
||||||
<?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 304 285"><svg id="d2-svg" class="d2-2067460405" width="304" height="285" viewBox="-101 -118 304 285"><rect x="-101.000000" y="-118.000000" width="304.000000" height="285.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
<?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.6.0-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 304 285"><svg id="d2-svg" class="d2-3111330921" width="304" height="285" viewBox="-101 -118 304 285"><rect x="-101.000000" y="-118.000000" width="304.000000" height="285.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
|
||||||
.appendix-icon {
|
.appendix-icon {
|
||||||
filter: drop-shadow(0px 0px 32px rgba(31, 36, 58, 0.1));
|
filter: drop-shadow(0px 0px 32px rgba(31, 36, 58, 0.1));
|
||||||
}
|
}
|
||||||
.d2-2067460405 .text-bold {
|
.d2-3111330921 .text-bold {
|
||||||
font-family: "d2-2067460405-font-bold";
|
font-family: "d2-3111330921-font-bold";
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: d2-2067460405-font-bold;
|
font-family: d2-3111330921-font-bold;
|
||||||
src: url("data:application/font-woff;base64,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");
|
src: url("data:application/font-woff;base64,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");
|
||||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||||
shape-rendering: geometricPrecision;
|
shape-rendering: geometricPrecision;
|
||||||
|
|
@ -21,78 +21,78 @@
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2-2067460405 .fill-N1{fill:#0A0F25;}
|
.d2-3111330921 .fill-N1{fill:#0A0F25;}
|
||||||
.d2-2067460405 .fill-N2{fill:#676C7E;}
|
.d2-3111330921 .fill-N2{fill:#676C7E;}
|
||||||
.d2-2067460405 .fill-N3{fill:#9499AB;}
|
.d2-3111330921 .fill-N3{fill:#9499AB;}
|
||||||
.d2-2067460405 .fill-N4{fill:#CFD2DD;}
|
.d2-3111330921 .fill-N4{fill:#CFD2DD;}
|
||||||
.d2-2067460405 .fill-N5{fill:#DEE1EB;}
|
.d2-3111330921 .fill-N5{fill:#DEE1EB;}
|
||||||
.d2-2067460405 .fill-N6{fill:#EEF1F8;}
|
.d2-3111330921 .fill-N6{fill:#EEF1F8;}
|
||||||
.d2-2067460405 .fill-N7{fill:#FFFFFF;}
|
.d2-3111330921 .fill-N7{fill:#FFFFFF;}
|
||||||
.d2-2067460405 .fill-B1{fill:#0D32B2;}
|
.d2-3111330921 .fill-B1{fill:#0D32B2;}
|
||||||
.d2-2067460405 .fill-B2{fill:#0D32B2;}
|
.d2-3111330921 .fill-B2{fill:#0D32B2;}
|
||||||
.d2-2067460405 .fill-B3{fill:#E3E9FD;}
|
.d2-3111330921 .fill-B3{fill:#E3E9FD;}
|
||||||
.d2-2067460405 .fill-B4{fill:#E3E9FD;}
|
.d2-3111330921 .fill-B4{fill:#E3E9FD;}
|
||||||
.d2-2067460405 .fill-B5{fill:#EDF0FD;}
|
.d2-3111330921 .fill-B5{fill:#EDF0FD;}
|
||||||
.d2-2067460405 .fill-B6{fill:#F7F8FE;}
|
.d2-3111330921 .fill-B6{fill:#F7F8FE;}
|
||||||
.d2-2067460405 .fill-AA2{fill:#4A6FF3;}
|
.d2-3111330921 .fill-AA2{fill:#4A6FF3;}
|
||||||
.d2-2067460405 .fill-AA4{fill:#EDF0FD;}
|
.d2-3111330921 .fill-AA4{fill:#EDF0FD;}
|
||||||
.d2-2067460405 .fill-AA5{fill:#F7F8FE;}
|
.d2-3111330921 .fill-AA5{fill:#F7F8FE;}
|
||||||
.d2-2067460405 .fill-AB4{fill:#EDF0FD;}
|
.d2-3111330921 .fill-AB4{fill:#EDF0FD;}
|
||||||
.d2-2067460405 .fill-AB5{fill:#F7F8FE;}
|
.d2-3111330921 .fill-AB5{fill:#F7F8FE;}
|
||||||
.d2-2067460405 .stroke-N1{stroke:#0A0F25;}
|
.d2-3111330921 .stroke-N1{stroke:#0A0F25;}
|
||||||
.d2-2067460405 .stroke-N2{stroke:#676C7E;}
|
.d2-3111330921 .stroke-N2{stroke:#676C7E;}
|
||||||
.d2-2067460405 .stroke-N3{stroke:#9499AB;}
|
.d2-3111330921 .stroke-N3{stroke:#9499AB;}
|
||||||
.d2-2067460405 .stroke-N4{stroke:#CFD2DD;}
|
.d2-3111330921 .stroke-N4{stroke:#CFD2DD;}
|
||||||
.d2-2067460405 .stroke-N5{stroke:#DEE1EB;}
|
.d2-3111330921 .stroke-N5{stroke:#DEE1EB;}
|
||||||
.d2-2067460405 .stroke-N6{stroke:#EEF1F8;}
|
.d2-3111330921 .stroke-N6{stroke:#EEF1F8;}
|
||||||
.d2-2067460405 .stroke-N7{stroke:#FFFFFF;}
|
.d2-3111330921 .stroke-N7{stroke:#FFFFFF;}
|
||||||
.d2-2067460405 .stroke-B1{stroke:#0D32B2;}
|
.d2-3111330921 .stroke-B1{stroke:#0D32B2;}
|
||||||
.d2-2067460405 .stroke-B2{stroke:#0D32B2;}
|
.d2-3111330921 .stroke-B2{stroke:#0D32B2;}
|
||||||
.d2-2067460405 .stroke-B3{stroke:#E3E9FD;}
|
.d2-3111330921 .stroke-B3{stroke:#E3E9FD;}
|
||||||
.d2-2067460405 .stroke-B4{stroke:#E3E9FD;}
|
.d2-3111330921 .stroke-B4{stroke:#E3E9FD;}
|
||||||
.d2-2067460405 .stroke-B5{stroke:#EDF0FD;}
|
.d2-3111330921 .stroke-B5{stroke:#EDF0FD;}
|
||||||
.d2-2067460405 .stroke-B6{stroke:#F7F8FE;}
|
.d2-3111330921 .stroke-B6{stroke:#F7F8FE;}
|
||||||
.d2-2067460405 .stroke-AA2{stroke:#4A6FF3;}
|
.d2-3111330921 .stroke-AA2{stroke:#4A6FF3;}
|
||||||
.d2-2067460405 .stroke-AA4{stroke:#EDF0FD;}
|
.d2-3111330921 .stroke-AA4{stroke:#EDF0FD;}
|
||||||
.d2-2067460405 .stroke-AA5{stroke:#F7F8FE;}
|
.d2-3111330921 .stroke-AA5{stroke:#F7F8FE;}
|
||||||
.d2-2067460405 .stroke-AB4{stroke:#EDF0FD;}
|
.d2-3111330921 .stroke-AB4{stroke:#EDF0FD;}
|
||||||
.d2-2067460405 .stroke-AB5{stroke:#F7F8FE;}
|
.d2-3111330921 .stroke-AB5{stroke:#F7F8FE;}
|
||||||
.d2-2067460405 .background-color-N1{background-color:#0A0F25;}
|
.d2-3111330921 .background-color-N1{background-color:#0A0F25;}
|
||||||
.d2-2067460405 .background-color-N2{background-color:#676C7E;}
|
.d2-3111330921 .background-color-N2{background-color:#676C7E;}
|
||||||
.d2-2067460405 .background-color-N3{background-color:#9499AB;}
|
.d2-3111330921 .background-color-N3{background-color:#9499AB;}
|
||||||
.d2-2067460405 .background-color-N4{background-color:#CFD2DD;}
|
.d2-3111330921 .background-color-N4{background-color:#CFD2DD;}
|
||||||
.d2-2067460405 .background-color-N5{background-color:#DEE1EB;}
|
.d2-3111330921 .background-color-N5{background-color:#DEE1EB;}
|
||||||
.d2-2067460405 .background-color-N6{background-color:#EEF1F8;}
|
.d2-3111330921 .background-color-N6{background-color:#EEF1F8;}
|
||||||
.d2-2067460405 .background-color-N7{background-color:#FFFFFF;}
|
.d2-3111330921 .background-color-N7{background-color:#FFFFFF;}
|
||||||
.d2-2067460405 .background-color-B1{background-color:#0D32B2;}
|
.d2-3111330921 .background-color-B1{background-color:#0D32B2;}
|
||||||
.d2-2067460405 .background-color-B2{background-color:#0D32B2;}
|
.d2-3111330921 .background-color-B2{background-color:#0D32B2;}
|
||||||
.d2-2067460405 .background-color-B3{background-color:#E3E9FD;}
|
.d2-3111330921 .background-color-B3{background-color:#E3E9FD;}
|
||||||
.d2-2067460405 .background-color-B4{background-color:#E3E9FD;}
|
.d2-3111330921 .background-color-B4{background-color:#E3E9FD;}
|
||||||
.d2-2067460405 .background-color-B5{background-color:#EDF0FD;}
|
.d2-3111330921 .background-color-B5{background-color:#EDF0FD;}
|
||||||
.d2-2067460405 .background-color-B6{background-color:#F7F8FE;}
|
.d2-3111330921 .background-color-B6{background-color:#F7F8FE;}
|
||||||
.d2-2067460405 .background-color-AA2{background-color:#4A6FF3;}
|
.d2-3111330921 .background-color-AA2{background-color:#4A6FF3;}
|
||||||
.d2-2067460405 .background-color-AA4{background-color:#EDF0FD;}
|
.d2-3111330921 .background-color-AA4{background-color:#EDF0FD;}
|
||||||
.d2-2067460405 .background-color-AA5{background-color:#F7F8FE;}
|
.d2-3111330921 .background-color-AA5{background-color:#F7F8FE;}
|
||||||
.d2-2067460405 .background-color-AB4{background-color:#EDF0FD;}
|
.d2-3111330921 .background-color-AB4{background-color:#EDF0FD;}
|
||||||
.d2-2067460405 .background-color-AB5{background-color:#F7F8FE;}
|
.d2-3111330921 .background-color-AB5{background-color:#F7F8FE;}
|
||||||
.d2-2067460405 .color-N1{color:#0A0F25;}
|
.d2-3111330921 .color-N1{color:#0A0F25;}
|
||||||
.d2-2067460405 .color-N2{color:#676C7E;}
|
.d2-3111330921 .color-N2{color:#676C7E;}
|
||||||
.d2-2067460405 .color-N3{color:#9499AB;}
|
.d2-3111330921 .color-N3{color:#9499AB;}
|
||||||
.d2-2067460405 .color-N4{color:#CFD2DD;}
|
.d2-3111330921 .color-N4{color:#CFD2DD;}
|
||||||
.d2-2067460405 .color-N5{color:#DEE1EB;}
|
.d2-3111330921 .color-N5{color:#DEE1EB;}
|
||||||
.d2-2067460405 .color-N6{color:#EEF1F8;}
|
.d2-3111330921 .color-N6{color:#EEF1F8;}
|
||||||
.d2-2067460405 .color-N7{color:#FFFFFF;}
|
.d2-3111330921 .color-N7{color:#FFFFFF;}
|
||||||
.d2-2067460405 .color-B1{color:#0D32B2;}
|
.d2-3111330921 .color-B1{color:#0D32B2;}
|
||||||
.d2-2067460405 .color-B2{color:#0D32B2;}
|
.d2-3111330921 .color-B2{color:#0D32B2;}
|
||||||
.d2-2067460405 .color-B3{color:#E3E9FD;}
|
.d2-3111330921 .color-B3{color:#E3E9FD;}
|
||||||
.d2-2067460405 .color-B4{color:#E3E9FD;}
|
.d2-3111330921 .color-B4{color:#E3E9FD;}
|
||||||
.d2-2067460405 .color-B5{color:#EDF0FD;}
|
.d2-3111330921 .color-B5{color:#EDF0FD;}
|
||||||
.d2-2067460405 .color-B6{color:#F7F8FE;}
|
.d2-3111330921 .color-B6{color:#F7F8FE;}
|
||||||
.d2-2067460405 .color-AA2{color:#4A6FF3;}
|
.d2-3111330921 .color-AA2{color:#4A6FF3;}
|
||||||
.d2-2067460405 .color-AA4{color:#EDF0FD;}
|
.d2-3111330921 .color-AA4{color:#EDF0FD;}
|
||||||
.d2-2067460405 .color-AA5{color:#F7F8FE;}
|
.d2-3111330921 .color-AA5{color:#F7F8FE;}
|
||||||
.d2-2067460405 .color-AB4{color:#EDF0FD;}
|
.d2-3111330921 .color-AB4{color:#EDF0FD;}
|
||||||
.d2-2067460405 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--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-bright);mix-blend-mode:darken}.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-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.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-bright);mix-blend-mode:darken}.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><a href="x/index.svg" xlink:href="x/index.svg"><g id="x"><g class="shape" ><rect x="0.000000" y="0.000000" width="85.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="42.500000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">x</text></g></a><g transform="translate(69 -16)" class="appendix-icon"><svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
.d2-3111330921 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--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-bright);mix-blend-mode:darken}.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-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.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-bright);mix-blend-mode:darken}.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><a href="x/index.svg" xlink:href="x/index.svg"><g id="x"><g class="shape" ><rect x="0.000000" y="0.000000" width="85.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="42.500000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">x</text></g></a><g transform="translate(69 -16)" class="appendix-icon"><svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g clip-path="url(#clip0_3440_35088111)">
|
<g clip-path="url(#clip0_3440_35088111)">
|
||||||
<path d="M16 31.1109C24.3456 31.1109 31.1111 24.3454 31.1111 15.9998C31.1111 7.65415 24.3456 0.888672 16 0.888672C7.65436 0.888672 0.888885 7.65415 0.888885 15.9998C0.888885 24.3454 7.65436 31.1109 16 31.1109Z" fill="white" stroke="#DEE1EB"/>
|
<path d="M16 31.1109C24.3456 31.1109 31.1111 24.3454 31.1111 15.9998C31.1111 7.65415 24.3456 0.888672 16 0.888672C7.65436 0.888672 0.888885 7.65415 0.888885 15.9998C0.888885 24.3454 7.65436 31.1109 16 31.1109Z" fill="white" stroke="#DEE1EB"/>
|
||||||
<path d="M14.3909 16.7965C14.7364 17.2584 15.1772 17.6406 15.6834 17.9171C16.1896 18.1938 16.7494 18.3582 17.3248 18.3993C17.9001 18.4405 18.4777 18.3575 19.0181 18.1559C19.5586 17.9543 20.0492 17.6389 20.4571 17.2309L22.8708 14.8173C23.6036 14.0586 24.0089 13.0425 23.9998 11.9877C23.9906 10.933 23.5676 9.92404 22.8217 9.17821C22.0759 8.43237 21.067 8.00931 20.0123 8.00015C18.9575 7.99098 17.9413 8.39644 17.1827 9.1292L15.7988 10.505" stroke="#2E3346" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M14.3909 16.7965C14.7364 17.2584 15.1772 17.6406 15.6834 17.9171C16.1896 18.1938 16.7494 18.3582 17.3248 18.3993C17.9001 18.4405 18.4777 18.3575 19.0181 18.1559C19.5586 17.9543 20.0492 17.6389 20.4571 17.2309L22.8708 14.8173C23.6036 14.0586 24.0089 13.0425 23.9998 11.9877C23.9906 10.933 23.5676 9.92404 22.8217 9.17821C22.0759 8.43237 21.067 8.00931 20.0123 8.00015C18.9575 7.99098 17.9413 8.39644 17.1827 9.1292L15.7988 10.505" stroke="#2E3346" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
|
@ -104,7 +104,7 @@
|
||||||
</clipPath>
|
</clipPath>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
</g><mask id="d2-2067460405" maskUnits="userSpaceOnUse" x="-101" y="-118" width="304" height="285">
|
</g><mask id="d2-3111330921" maskUnits="userSpaceOnUse" x="-101" y="-118" width="304" height="285">
|
||||||
<rect x="-101" y="-118" width="304" height="285" fill="white"></rect>
|
<rect x="-101" y="-118" width="304" height="285" fill="white"></rect>
|
||||||
<rect x="38.500000" y="22.500000" width="8" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
<rect x="38.500000" y="22.500000" width="8" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||||
</mask></svg></svg>
|
</mask></svg></svg>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
152
e2etests-cli/testdata/TestCLI_E2E/center.exp.svg
vendored
|
|
@ -1,9 +1,9 @@
|
||||||
<?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="xMidYMid 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[
|
<?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.6.0-HEAD" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 434"><svg id="d2-svg" class="d2-1843626214" 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 {
|
.d2-1843626214 .text-bold {
|
||||||
font-family: "d2-855222762-font-bold";
|
font-family: "d2-1843626214-font-bold";
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: d2-855222762-font-bold;
|
font-family: d2-1843626214-font-bold;
|
||||||
src: url("data:application/font-woff;base64,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");
|
src: url("data:application/font-woff;base64,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");
|
||||||
}]]></style><style type="text/css"><![CDATA[.shape {
|
}]]></style><style type="text/css"><![CDATA[.shape {
|
||||||
shape-rendering: geometricPrecision;
|
shape-rendering: geometricPrecision;
|
||||||
|
|
@ -18,78 +18,78 @@
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.d2-855222762 .fill-N1{fill:#0A0F25;}
|
.d2-1843626214 .fill-N1{fill:#0A0F25;}
|
||||||
.d2-855222762 .fill-N2{fill:#676C7E;}
|
.d2-1843626214 .fill-N2{fill:#676C7E;}
|
||||||
.d2-855222762 .fill-N3{fill:#9499AB;}
|
.d2-1843626214 .fill-N3{fill:#9499AB;}
|
||||||
.d2-855222762 .fill-N4{fill:#CFD2DD;}
|
.d2-1843626214 .fill-N4{fill:#CFD2DD;}
|
||||||
.d2-855222762 .fill-N5{fill:#DEE1EB;}
|
.d2-1843626214 .fill-N5{fill:#DEE1EB;}
|
||||||
.d2-855222762 .fill-N6{fill:#EEF1F8;}
|
.d2-1843626214 .fill-N6{fill:#EEF1F8;}
|
||||||
.d2-855222762 .fill-N7{fill:#FFFFFF;}
|
.d2-1843626214 .fill-N7{fill:#FFFFFF;}
|
||||||
.d2-855222762 .fill-B1{fill:#0D32B2;}
|
.d2-1843626214 .fill-B1{fill:#0D32B2;}
|
||||||
.d2-855222762 .fill-B2{fill:#0D32B2;}
|
.d2-1843626214 .fill-B2{fill:#0D32B2;}
|
||||||
.d2-855222762 .fill-B3{fill:#E3E9FD;}
|
.d2-1843626214 .fill-B3{fill:#E3E9FD;}
|
||||||
.d2-855222762 .fill-B4{fill:#E3E9FD;}
|
.d2-1843626214 .fill-B4{fill:#E3E9FD;}
|
||||||
.d2-855222762 .fill-B5{fill:#EDF0FD;}
|
.d2-1843626214 .fill-B5{fill:#EDF0FD;}
|
||||||
.d2-855222762 .fill-B6{fill:#F7F8FE;}
|
.d2-1843626214 .fill-B6{fill:#F7F8FE;}
|
||||||
.d2-855222762 .fill-AA2{fill:#4A6FF3;}
|
.d2-1843626214 .fill-AA2{fill:#4A6FF3;}
|
||||||
.d2-855222762 .fill-AA4{fill:#EDF0FD;}
|
.d2-1843626214 .fill-AA4{fill:#EDF0FD;}
|
||||||
.d2-855222762 .fill-AA5{fill:#F7F8FE;}
|
.d2-1843626214 .fill-AA5{fill:#F7F8FE;}
|
||||||
.d2-855222762 .fill-AB4{fill:#EDF0FD;}
|
.d2-1843626214 .fill-AB4{fill:#EDF0FD;}
|
||||||
.d2-855222762 .fill-AB5{fill:#F7F8FE;}
|
.d2-1843626214 .fill-AB5{fill:#F7F8FE;}
|
||||||
.d2-855222762 .stroke-N1{stroke:#0A0F25;}
|
.d2-1843626214 .stroke-N1{stroke:#0A0F25;}
|
||||||
.d2-855222762 .stroke-N2{stroke:#676C7E;}
|
.d2-1843626214 .stroke-N2{stroke:#676C7E;}
|
||||||
.d2-855222762 .stroke-N3{stroke:#9499AB;}
|
.d2-1843626214 .stroke-N3{stroke:#9499AB;}
|
||||||
.d2-855222762 .stroke-N4{stroke:#CFD2DD;}
|
.d2-1843626214 .stroke-N4{stroke:#CFD2DD;}
|
||||||
.d2-855222762 .stroke-N5{stroke:#DEE1EB;}
|
.d2-1843626214 .stroke-N5{stroke:#DEE1EB;}
|
||||||
.d2-855222762 .stroke-N6{stroke:#EEF1F8;}
|
.d2-1843626214 .stroke-N6{stroke:#EEF1F8;}
|
||||||
.d2-855222762 .stroke-N7{stroke:#FFFFFF;}
|
.d2-1843626214 .stroke-N7{stroke:#FFFFFF;}
|
||||||
.d2-855222762 .stroke-B1{stroke:#0D32B2;}
|
.d2-1843626214 .stroke-B1{stroke:#0D32B2;}
|
||||||
.d2-855222762 .stroke-B2{stroke:#0D32B2;}
|
.d2-1843626214 .stroke-B2{stroke:#0D32B2;}
|
||||||
.d2-855222762 .stroke-B3{stroke:#E3E9FD;}
|
.d2-1843626214 .stroke-B3{stroke:#E3E9FD;}
|
||||||
.d2-855222762 .stroke-B4{stroke:#E3E9FD;}
|
.d2-1843626214 .stroke-B4{stroke:#E3E9FD;}
|
||||||
.d2-855222762 .stroke-B5{stroke:#EDF0FD;}
|
.d2-1843626214 .stroke-B5{stroke:#EDF0FD;}
|
||||||
.d2-855222762 .stroke-B6{stroke:#F7F8FE;}
|
.d2-1843626214 .stroke-B6{stroke:#F7F8FE;}
|
||||||
.d2-855222762 .stroke-AA2{stroke:#4A6FF3;}
|
.d2-1843626214 .stroke-AA2{stroke:#4A6FF3;}
|
||||||
.d2-855222762 .stroke-AA4{stroke:#EDF0FD;}
|
.d2-1843626214 .stroke-AA4{stroke:#EDF0FD;}
|
||||||
.d2-855222762 .stroke-AA5{stroke:#F7F8FE;}
|
.d2-1843626214 .stroke-AA5{stroke:#F7F8FE;}
|
||||||
.d2-855222762 .stroke-AB4{stroke:#EDF0FD;}
|
.d2-1843626214 .stroke-AB4{stroke:#EDF0FD;}
|
||||||
.d2-855222762 .stroke-AB5{stroke:#F7F8FE;}
|
.d2-1843626214 .stroke-AB5{stroke:#F7F8FE;}
|
||||||
.d2-855222762 .background-color-N1{background-color:#0A0F25;}
|
.d2-1843626214 .background-color-N1{background-color:#0A0F25;}
|
||||||
.d2-855222762 .background-color-N2{background-color:#676C7E;}
|
.d2-1843626214 .background-color-N2{background-color:#676C7E;}
|
||||||
.d2-855222762 .background-color-N3{background-color:#9499AB;}
|
.d2-1843626214 .background-color-N3{background-color:#9499AB;}
|
||||||
.d2-855222762 .background-color-N4{background-color:#CFD2DD;}
|
.d2-1843626214 .background-color-N4{background-color:#CFD2DD;}
|
||||||
.d2-855222762 .background-color-N5{background-color:#DEE1EB;}
|
.d2-1843626214 .background-color-N5{background-color:#DEE1EB;}
|
||||||
.d2-855222762 .background-color-N6{background-color:#EEF1F8;}
|
.d2-1843626214 .background-color-N6{background-color:#EEF1F8;}
|
||||||
.d2-855222762 .background-color-N7{background-color:#FFFFFF;}
|
.d2-1843626214 .background-color-N7{background-color:#FFFFFF;}
|
||||||
.d2-855222762 .background-color-B1{background-color:#0D32B2;}
|
.d2-1843626214 .background-color-B1{background-color:#0D32B2;}
|
||||||
.d2-855222762 .background-color-B2{background-color:#0D32B2;}
|
.d2-1843626214 .background-color-B2{background-color:#0D32B2;}
|
||||||
.d2-855222762 .background-color-B3{background-color:#E3E9FD;}
|
.d2-1843626214 .background-color-B3{background-color:#E3E9FD;}
|
||||||
.d2-855222762 .background-color-B4{background-color:#E3E9FD;}
|
.d2-1843626214 .background-color-B4{background-color:#E3E9FD;}
|
||||||
.d2-855222762 .background-color-B5{background-color:#EDF0FD;}
|
.d2-1843626214 .background-color-B5{background-color:#EDF0FD;}
|
||||||
.d2-855222762 .background-color-B6{background-color:#F7F8FE;}
|
.d2-1843626214 .background-color-B6{background-color:#F7F8FE;}
|
||||||
.d2-855222762 .background-color-AA2{background-color:#4A6FF3;}
|
.d2-1843626214 .background-color-AA2{background-color:#4A6FF3;}
|
||||||
.d2-855222762 .background-color-AA4{background-color:#EDF0FD;}
|
.d2-1843626214 .background-color-AA4{background-color:#EDF0FD;}
|
||||||
.d2-855222762 .background-color-AA5{background-color:#F7F8FE;}
|
.d2-1843626214 .background-color-AA5{background-color:#F7F8FE;}
|
||||||
.d2-855222762 .background-color-AB4{background-color:#EDF0FD;}
|
.d2-1843626214 .background-color-AB4{background-color:#EDF0FD;}
|
||||||
.d2-855222762 .background-color-AB5{background-color:#F7F8FE;}
|
.d2-1843626214 .background-color-AB5{background-color:#F7F8FE;}
|
||||||
.d2-855222762 .color-N1{color:#0A0F25;}
|
.d2-1843626214 .color-N1{color:#0A0F25;}
|
||||||
.d2-855222762 .color-N2{color:#676C7E;}
|
.d2-1843626214 .color-N2{color:#676C7E;}
|
||||||
.d2-855222762 .color-N3{color:#9499AB;}
|
.d2-1843626214 .color-N3{color:#9499AB;}
|
||||||
.d2-855222762 .color-N4{color:#CFD2DD;}
|
.d2-1843626214 .color-N4{color:#CFD2DD;}
|
||||||
.d2-855222762 .color-N5{color:#DEE1EB;}
|
.d2-1843626214 .color-N5{color:#DEE1EB;}
|
||||||
.d2-855222762 .color-N6{color:#EEF1F8;}
|
.d2-1843626214 .color-N6{color:#EEF1F8;}
|
||||||
.d2-855222762 .color-N7{color:#FFFFFF;}
|
.d2-1843626214 .color-N7{color:#FFFFFF;}
|
||||||
.d2-855222762 .color-B1{color:#0D32B2;}
|
.d2-1843626214 .color-B1{color:#0D32B2;}
|
||||||
.d2-855222762 .color-B2{color:#0D32B2;}
|
.d2-1843626214 .color-B2{color:#0D32B2;}
|
||||||
.d2-855222762 .color-B3{color:#E3E9FD;}
|
.d2-1843626214 .color-B3{color:#E3E9FD;}
|
||||||
.d2-855222762 .color-B4{color:#E3E9FD;}
|
.d2-1843626214 .color-B4{color:#E3E9FD;}
|
||||||
.d2-855222762 .color-B5{color:#EDF0FD;}
|
.d2-1843626214 .color-B5{color:#EDF0FD;}
|
||||||
.d2-855222762 .color-B6{color:#F7F8FE;}
|
.d2-1843626214 .color-B6{color:#F7F8FE;}
|
||||||
.d2-855222762 .color-AA2{color:#4A6FF3;}
|
.d2-1843626214 .color-AA2{color:#4A6FF3;}
|
||||||
.d2-855222762 .color-AA4{color:#EDF0FD;}
|
.d2-1843626214 .color-AA4{color:#EDF0FD;}
|
||||||
.d2-855222762 .color-AA5{color:#F7F8FE;}
|
.d2-1843626214 .color-AA5{color:#F7F8FE;}
|
||||||
.d2-855222762 .color-AB4{color:#EDF0FD;}
|
.d2-1843626214 .color-AB4{color:#EDF0FD;}
|
||||||
.d2-855222762 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--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-bright);mix-blend-mode:darken}.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-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.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-bright);mix-blend-mode:darken}.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><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">
|
.d2-1843626214 .color-AB5{color:#F7F8FE;}.appendix text.text{fill:#0A0F25}.md{--color-fg-default:#0A0F25;--color-fg-muted:#676C7E;--color-fg-subtle:#9499AB;--color-canvas-default:#FFFFFF;--color-canvas-subtle:#EEF1F8;--color-border-default:#0D32B2;--color-border-muted:#0D32B2;--color-neutral-muted:#EEF1F8;--color-accent-fg:#0D32B2;--color-accent-emphasis:#0D32B2;--color-attention-subtle:#676C7E;--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-bright);mix-blend-mode:darken}.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-bright);mix-blend-mode:darken}.sketch-overlay-AA5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB4{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-AB5{fill:url(#streaks-bright);mix-blend-mode:darken}.sketch-overlay-N1{fill:url(#streaks-darker);mix-blend-mode:lighten}.sketch-overlay-N2{fill:url(#streaks-dark);mix-blend-mode:overlay}.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-bright);mix-blend-mode:darken}.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><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-1843626214)" /></g><mask id="d2-1843626214" maskUnits="userSpaceOnUse" x="-101" y="-101" width="256" height="434">
|
||||||
<rect x="-101" y="-101" width="256" height="434" fill="white"></rect>
|
<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="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>
|
<rect x="22.500000" y="188.500000" width="9" height="21" fill="rgba(0,0,0,0.75)"></rect>
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.5 KiB |