Merge branch 'terrastruct:master' into master

This commit is contained in:
Barry Nolte 2023-11-16 10:09:57 -08:00 committed by GitHub
commit 6dc911ed17
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
72 changed files with 25407 additions and 1863 deletions

View file

@ -221,6 +221,7 @@ let us know and we'll be happy to include it here!
- **Python D2 diagram builder**: [https://github.com/MrBlenny/py-d2](https://github.com/MrBlenny/py-d2)
- **Clojure D2 transpiler**: [https://github.com/judepayne/dictim](https://github.com/judepayne/dictim)
- **JavaScript D2 diagram builder**: [https://github.com/Kreshnik/d2lang-js](https://github.com/Kreshnik/d2lang-js)
- **C# & dotnet SDK**: [https://github.com/Stephanvs/d2lang-cs](https://github.com/Stephanvs/d2lang-cs)
- **Maven plugin**: [https://github.com/andrinmeier/unofficial-d2lang-maven-plugin](https://github.com/andrinmeier/unofficial-d2lang-maven-plugin)
- **Confluence plugin**: [https://github.com/andrinmeier/unofficial-d2lang-confluence-plugin](https://github.com/andrinmeier/unofficial-d2lang-confluence-plugin)
- **CIL (C#, Visual Basic, F#, C++ CLR) to D2**: [https://github.com/HugoVG/AppDiagram](https://github.com/HugoVG/AppDiagram)

View file

@ -1,12 +1,18 @@
#### Features 🚀
- ELK now routes `sql_table` edges to the exact columns (ty @landmaj) [#1681](https://github.com/terrastruct/d2/pull/1681)
- Adds new unfilled triangle arrowhead. [#1711](https://github.com/terrastruct/d2/pull/1711)
- Grid containers can now have custom label positions. [#1715](https://github.com/terrastruct/d2/pull/1715)
#### Improvements 🧹
- Grid cells can now contain nested edges [#1629](https://github.com/terrastruct/d2/pull/1629)
- Edges can now go across constant nears, sequence diagrams, and grids including nested ones. [#1631](https://github.com/terrastruct/d2/pull/1631)
- All vars defined in a scope are accessible everywhere in that scope, i.e., an object can use a var defined after itself. [#1695](https://github.com/terrastruct/d2/pull/1695)
- Encoding API switches to standard zlib encoding so that decoding doesn't depend on source. [#1709](https://github.com/terrastruct/d2/pull/1709)
- `currentcolor` is accepted as a color option to inherit parent colors. (ty @hboomsma) [#1700](https://github.com/terrastruct/d2/pull/1700)
- grid containers can now be sized with `width`/`height` even when using a layout plugin without that feature. [#1731](https://github.com/terrastruct/d2/pull/1731)
- Watch mode watches for changes in both the input file and imported files [#1720](https://github.com/terrastruct/d2/pull/1720)
#### Bugfixes ⛑️
@ -19,3 +25,7 @@
- Adds a compiler error when accidentally using an arrowhead on a shape [#1686](https://github.com/terrastruct/d2/pull/1686)
- Correctly reports errors from invalid values set by globs. [#1691](https://github.com/terrastruct/d2/pull/1691)
- Fixes panic when spread substitution referenced a nonexistant var. [#1695](https://github.com/terrastruct/d2/pull/1695)
- Fixes incorrect appendix icon numbering. [#1704](https://github.com/terrastruct/d2/pull/1704)
- Fixes crash when using `--watch` and navigating to an invalid board path [#1693](https://github.com/terrastruct/d2/pull/1693)
- Fixes edge case where nested edge globs were creating excess shapes [#1713](https://github.com/terrastruct/d2/pull/1713)
- Fixes a panic with a connection to a grid cell that is a container in TALA [#1729](https://github.com/terrastruct/d2/pull/1729)

View file

@ -17,7 +17,7 @@ import (
func fmtCmd(ctx context.Context, ms *xmain.State) (err error) {
defer xdefer.Errorf(&err, "failed to fmt")
ms.Opts = xmain.NewOpts(ms.Env, ms.Log, ms.Opts.Flags.Args()[1:])
ms.Opts = xmain.NewOpts(ms.Env, ms.Opts.Flags.Args()[1:])
if len(ms.Opts.Args) == 0 {
return xmain.UsageErrorf("fmt must be passed at least one file to be formatted")
}

View file

@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io"
"io/fs"
"os"
"os/exec"
"os/user"
@ -332,7 +333,7 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
ctx, cancel := timelib.WithTimeout(ctx, time.Minute*2)
defer cancel()
_, written, err := compile(ctx, ms, plugins, layoutFlag, renderOpts, fontFamily, *animateIntervalFlag, inputPath, outputPath, "", *bundleFlag, *forceAppendixFlag, pw.Page)
_, written, err := compile(ctx, ms, plugins, nil, layoutFlag, renderOpts, fontFamily, *animateIntervalFlag, inputPath, outputPath, "", *bundleFlag, *forceAppendixFlag, pw.Page)
if err != nil {
if written {
return fmt.Errorf("failed to fully compile (partial render written) %s: %w", ms.HumanPath(inputPath), err)
@ -367,7 +368,7 @@ func LayoutResolver(ctx context.Context, ms *xmain.State, plugins []d2plugin.Plu
}
}
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) {
func compile(ctx context.Context, ms *xmain.State, plugins []d2plugin.Plugin, fs fs.FS, 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()
input, err := ms.ReadPath(inputPath)
if err != nil {
@ -385,6 +386,7 @@ func compile(ctx context.Context, ms *xmain.State, plugins []d2plugin.Plugin, la
InputPath: inputPath,
LayoutResolver: LayoutResolver(ctx, ms, plugins),
Layout: layout,
FS: fs,
}
cancel := background.Repeat(func() {
@ -503,7 +505,7 @@ func compile(ctx context.Context, ms *xmain.State, plugins []d2plugin.Plugin, la
board := diagram.GetBoard(boardPath)
if board == nil {
return nil, false, fmt.Errorf("Diagram with path %s not found", boardPath)
return nil, false, fmt.Errorf(`Diagram with path "%s" not found. Did you mean to specify a board like "layers.%s"?`, boardPath, boardPath)
}
boards, err := render(ctx, ms, compileDur, plugin, renderOpts, inputPath, outputPath, bundle, forceAppendix, page, ruler, board)

View file

@ -12,6 +12,7 @@ import (
"os"
"path/filepath"
"runtime"
"sort"
"strings"
"sync"
"time"
@ -73,6 +74,7 @@ type watcher struct {
l net.Listener
staticFileServer http.Handler
boardpathMu sync.Mutex
wsclientsMu sync.Mutex
closing bool
wsclientsWG sync.WaitGroup
@ -218,10 +220,13 @@ func (w *watcher) goFunc(fn func(context.Context) error) {
* TODO: Abstract out file system and fsnotify to test this with 100% coverage. See comment in main_test.go
*/
func (w *watcher) watchLoop(ctx context.Context) error {
lastModified, err := w.ensureAddWatch(ctx)
lastModified := make(map[string]time.Time)
mt, err := w.ensureAddWatch(ctx, w.inputPath)
if err != nil {
return err
}
lastModified[w.inputPath] = mt
w.ms.Log.Info.Printf("compiling %v...", w.ms.HumanPath(w.inputPath))
w.requestCompile()
@ -230,6 +235,8 @@ func (w *watcher) watchLoop(ctx context.Context) error {
pollTicker := time.NewTicker(time.Second * 10)
defer pollTicker.Stop()
changed := make(map[string]struct{})
for {
select {
case <-pollTicker.C:
@ -237,13 +244,18 @@ func (w *watcher) watchLoop(ctx context.Context) error {
// getting any more events.
// File notification APIs are notoriously unreliable. I've personally experienced
// many quirks and so feel this check is justified even if excessive.
mt, err := w.ensureAddWatch(ctx)
if err != nil {
return err
missedChanges := false
for _, watched := range w.fw.WatchList() {
mt, err := w.ensureAddWatch(ctx, watched)
if err != nil {
return err
}
if mt2, ok := lastModified[watched]; !ok || !mt.Equal(mt2) {
missedChanges = true
lastModified[watched] = mt
}
}
if !mt.Equal(lastModified) {
// We missed changes.
lastModified = mt
if missedChanges {
w.requestCompile()
}
case ev, ok := <-w.fw.Events:
@ -251,19 +263,20 @@ func (w *watcher) watchLoop(ctx context.Context) error {
return errors.New("fsnotify watcher closed")
}
w.ms.Log.Debug.Printf("received file system event %v", ev)
mt, err := w.ensureAddWatch(ctx)
mt, err := w.ensureAddWatch(ctx, ev.Name)
if err != nil {
return err
}
if ev.Op == fsnotify.Chmod {
if mt.Equal(lastModified) {
if mt.Equal(lastModified[ev.Name]) {
// Benign Chmod.
// See https://github.com/fsnotify/fsnotify/issues/15
continue
}
// We missed changes.
lastModified = mt
lastModified[ev.Name] = mt
}
changed[ev.Name] = struct{}{}
// The purpose of eatBurstTimer is to wait at least 16 milliseconds after a sequence of
// events to ensure that whomever is editing the file is now done.
//
@ -276,8 +289,18 @@ func (w *watcher) watchLoop(ctx context.Context) error {
// misleading error.
eatBurstTimer.Reset(time.Millisecond * 16)
case <-eatBurstTimer.C:
w.ms.Log.Info.Printf("detected change in %v: recompiling...", w.ms.HumanPath(w.inputPath))
var changedList []string
for k := range changed {
changedList = append(changedList, k)
}
sort.Strings(changedList)
changedStr := w.ms.HumanPath(changedList[0])
for i := 1; i < len(changed); i++ {
changedStr += fmt.Sprintf(", %s", w.ms.HumanPath(changedList[i]))
}
w.ms.Log.Info.Printf("detected change in %s: recompiling...", changedStr)
w.requestCompile()
changed = make(map[string]struct{})
case err, ok := <-w.fw.Errors:
if !ok {
return errors.New("fsnotify watcher closed")
@ -296,17 +319,17 @@ func (w *watcher) requestCompile() {
}
}
func (w *watcher) ensureAddWatch(ctx context.Context) (time.Time, error) {
func (w *watcher) ensureAddWatch(ctx context.Context, path string) (time.Time, error) {
interval := time.Millisecond * 16
tc := time.NewTimer(0)
<-tc.C
for {
mt, err := w.addWatch(ctx)
mt, err := w.addWatch(ctx, path)
if err == nil {
return mt, nil
}
if interval >= time.Second {
w.ms.Log.Error.Printf("failed to watch inputPath %q: %v (retrying in %v)", w.ms.HumanPath(w.inputPath), err, interval)
w.ms.Log.Error.Printf("failed to watch %q: %v (retrying in %v)", w.ms.HumanPath(path), err, interval)
}
tc.Reset(interval)
@ -324,19 +347,56 @@ func (w *watcher) ensureAddWatch(ctx context.Context) (time.Time, error) {
}
}
func (w *watcher) addWatch(ctx context.Context) (time.Time, error) {
err := w.fw.Add(w.inputPath)
func (w *watcher) addWatch(ctx context.Context, path string) (time.Time, error) {
err := w.fw.Add(path)
if err != nil {
return time.Time{}, err
}
var d os.FileInfo
d, err = os.Stat(w.inputPath)
d, err = os.Stat(path)
if err != nil {
return time.Time{}, err
}
return d.ModTime(), nil
}
func (w *watcher) replaceWatchList(ctx context.Context, paths []string) error {
// First remove the files no longer being watched
for _, watched := range w.fw.WatchList() {
if watched == w.inputPath {
continue
}
found := false
for _, p := range paths {
if watched == p {
found = true
break
}
}
if !found {
// Don't mind errors here
w.fw.Remove(watched)
}
}
// Then add the files newly being watched
for _, p := range paths {
found := false
for _, watched := range w.fw.WatchList() {
if watched == p {
found = true
break
}
}
if !found {
_, err := w.ensureAddWatch(ctx, p)
if err != nil {
return err
}
}
}
return nil
}
func (w *watcher) compileLoop(ctx context.Context) error {
firstCompile := true
for {
@ -364,7 +424,10 @@ func (w *watcher) compileLoop(ctx context.Context) error {
w.pw = newPW
}
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)
fs := trackedFS{}
w.boardpathMu.Lock()
svg, _, err := compile(ctx, w.ms, w.plugins, &fs, w.layout, w.renderOpts, w.fontFamily, w.animateInterval, w.inputPath, w.outputPath, w.boardPath, w.bundle, w.forceAppendix, w.pw.Page)
w.boardpathMu.Unlock()
errs := ""
if err != nil {
if len(svg) > 0 {
@ -375,6 +438,11 @@ func (w *watcher) compileLoop(ctx context.Context) error {
errs = err.Error()
w.ms.Log.Error.Print(errs)
}
err = w.replaceWatchList(ctx, fs.opened)
if err != nil {
return err
}
w.broadcast(&compileResult{
SVG: string(svg),
Scale: w.renderOpts.Scale,
@ -442,13 +510,19 @@ func (w *watcher) handleRoot(hw http.ResponseWriter, r *http.Request) {
</body>
</html>`, filepath.Base(w.outputPath), w.devMode)
w.boardpathMu.Lock()
// 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]
}
recompile := false
if boardPath != w.boardPath {
w.boardPath = boardPath
recompile = true
}
w.boardpathMu.Unlock()
if recompile {
w.requestCompile()
}
}
@ -574,3 +648,16 @@ func wsHeartbeat(ctx context.Context, c *websocket.Conn) {
}
}
}
// trackedFS is OS's FS with the addition that it tracks which files are opened successfully
type trackedFS struct {
opened []string
}
func (tfs *trackedFS) Open(name string) (fs.File, error) {
f, err := os.Open(name)
if err == nil {
tfs.opened = append(tfs.opened, name)
}
return f, err
}

View file

@ -4297,6 +4297,23 @@ x: {
`, `d2/testdata/d2compiler/TestCompile2/globs/double-glob-override-err-val.d2:6:2: invalid "near" field`)
},
},
{
name: "creating-node-bug",
run: func(t *testing.T) {
g, _ := assertCompile(t, `
*.*a -> *.*b
container_1: {
a
}
container_2: {
b
}
`, ``)
assert.Equal(t, 4, len(g.Objects))
},
},
}
for _, tc := range tca {

View file

@ -202,7 +202,7 @@ func toConnection(edge *d2graph.Edge, theme *d2themes.Theme) d2target.Connection
if edge.SrcArrow {
connection.SrcArrow = d2target.DefaultArrowhead
if edge.SrcArrowhead != nil && edge.SrcArrowhead.Shape.Value != "" {
if edge.SrcArrowhead != nil {
connection.SrcArrow = edge.SrcArrowhead.ToArrowhead()
}
}
@ -220,7 +220,7 @@ func toConnection(edge *d2graph.Edge, theme *d2themes.Theme) d2target.Connection
}
if edge.DstArrow {
connection.DstArrow = d2target.DefaultArrowhead
if edge.DstArrowhead != nil && edge.DstArrowhead.Shape.Value != "" {
if edge.DstArrowhead != nil {
connection.DstArrow = edge.DstArrowhead.ToArrowhead()
}
}

View file

@ -4,6 +4,7 @@ import "regexp"
// namedColors is a list of valid CSS colors
var namedColors = []string{
"currentcolor",
"transparent",
"aliceblue",
"antiquewhite",

View file

@ -177,13 +177,10 @@ func (a *Attributes) ApplyTextTransform() {
}
func (a *Attributes) ToArrowhead() d2target.Arrowhead {
if a.Shape.Value == "" {
return d2target.NoArrowhead
}
filled := false
var filled *bool
if a.Style.Filled != nil {
filled, _ = strconv.ParseBool(a.Style.Filled.Value)
v, _ := strconv.ParseBool(a.Style.Filled.Value)
filled = go2.Pointer(v)
}
return d2target.ToArrowhead(a.Shape.Value, filled)
}
@ -1410,12 +1407,12 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
if obj.HasLabel() && obj.Attributes.LabelPosition != nil {
scalar := *obj.Attributes.LabelPosition
position := LabelPositionsMapping[scalar.Value]
obj.LabelPosition = go2.Pointer(string(position))
obj.LabelPosition = go2.Pointer(position.String())
}
if obj.Icon != nil && obj.Attributes.IconPosition != nil {
scalar := *obj.Attributes.IconPosition
position := LabelPositionsMapping[scalar.Value]
obj.IconPosition = go2.Pointer(string(position))
obj.IconPosition = go2.Pointer(position.String())
}
var desiredWidth int
@ -1941,3 +1938,72 @@ func (obj *Object) IsMultiple() bool {
func (obj *Object) Is3D() bool {
return obj.Style.ThreeDee != nil && obj.Style.ThreeDee.Value == "true"
}
func (obj *Object) Spacing() (margin, padding geo.Spacing) {
if obj.HasLabel() {
var position label.Position
if obj.LabelPosition != nil {
position = label.FromString(*obj.LabelPosition)
}
var labelWidth, labelHeight float64
if obj.LabelDimensions.Width > 0 {
labelWidth = float64(obj.LabelDimensions.Width) + 2*label.PADDING
}
if obj.LabelDimensions.Height > 0 {
labelHeight = float64(obj.LabelDimensions.Height) + 2*label.PADDING
}
switch position {
case label.OutsideTopLeft, label.OutsideTopCenter, label.OutsideTopRight:
margin.Top = labelHeight
case label.OutsideBottomLeft, label.OutsideBottomCenter, label.OutsideBottomRight:
margin.Bottom = labelHeight
case label.OutsideLeftTop, label.OutsideLeftMiddle, label.OutsideLeftBottom:
margin.Left = labelWidth
case label.OutsideRightTop, label.OutsideRightMiddle, label.OutsideRightBottom:
margin.Right = labelWidth
case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight:
padding.Top = labelHeight
case label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight:
padding.Bottom = labelHeight
case label.InsideMiddleLeft:
padding.Left = labelWidth
case label.InsideMiddleRight:
padding.Right = labelWidth
}
}
if obj.Icon != nil && obj.Shape.Value != d2target.ShapeImage {
var position label.Position
if obj.IconPosition != nil {
position = label.FromString(*obj.IconPosition)
}
iconSize := float64(d2target.MAX_ICON_SIZE + 2*label.PADDING)
switch position {
case label.OutsideTopLeft, label.OutsideTopCenter, label.OutsideTopRight:
margin.Top = math.Max(margin.Top, iconSize)
case label.OutsideBottomLeft, label.OutsideBottomCenter, label.OutsideBottomRight:
margin.Bottom = math.Max(margin.Bottom, iconSize)
case label.OutsideLeftTop, label.OutsideLeftMiddle, label.OutsideLeftBottom:
margin.Left = math.Max(margin.Left, iconSize)
case label.OutsideRightTop, label.OutsideRightMiddle, label.OutsideRightBottom:
margin.Right = math.Max(margin.Right, iconSize)
case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight:
padding.Top = math.Max(padding.Top, iconSize)
case label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight:
padding.Bottom = math.Max(padding.Bottom, iconSize)
case label.InsideMiddleLeft:
padding.Left = math.Max(padding.Left, iconSize)
case label.InsideMiddleRight:
padding.Right = math.Max(padding.Right, iconSize)
}
}
dx, dy := obj.GetModifierElementAdjustments()
margin.Right += dx
margin.Top += dy
return
}

View file

@ -290,7 +290,7 @@ func (obj *Object) GetMargin() geo.Spacing {
margin := geo.Spacing{}
if obj.HasLabel() && obj.LabelPosition != nil {
position := label.Position(*obj.LabelPosition)
position := label.FromString(*obj.LabelPosition)
labelWidth := float64(obj.LabelDimensions.Width + label.PADDING)
labelHeight := float64(obj.LabelDimensions.Height + label.PADDING)
@ -335,7 +335,7 @@ func (obj *Object) GetMargin() geo.Spacing {
}
if obj.Icon != nil && obj.IconPosition != nil && obj.Shape.Value != d2target.ShapeImage {
position := label.Position(*obj.IconPosition)
position := label.FromString(*obj.IconPosition)
iconSize := float64(d2target.MAX_ICON_SIZE + label.PADDING)
switch position {
@ -373,7 +373,7 @@ func (obj *Object) GetLabelTopLeft() *geo.Point {
}
s := obj.ToShape()
labelPosition := label.Position(*obj.LabelPosition)
labelPosition := label.FromString(*obj.LabelPosition)
var box *geo.Box
if labelPosition.IsOutside() {
@ -395,7 +395,7 @@ func (obj *Object) GetIconTopLeft() *geo.Point {
}
s := obj.ToShape()
iconPosition := label.Position(*obj.IconPosition)
iconPosition := label.FromString(*obj.IconPosition)
var box *geo.Box
if iconPosition.IsOutside() {
@ -416,7 +416,7 @@ func (edge *Edge) TraceToShape(points []*geo.Point, startIndex, endIndex int) (n
overlapsOutsideLabel := false
if edge.Src.HasLabel() {
// assumes LabelPosition, LabelWidth, LabelHeight are all set if there is a label
labelPosition := label.Position(*edge.Src.LabelPosition)
labelPosition := label.FromString(*edge.Src.LabelPosition)
if labelPosition.IsOutside() {
labelWidth := float64(edge.Src.LabelDimensions.Width)
labelHeight := float64(edge.Src.LabelDimensions.Height)
@ -467,7 +467,7 @@ func (edge *Edge) TraceToShape(points []*geo.Point, startIndex, endIndex int) (n
overlapsOutsideLabel = false
if edge.Dst.HasLabel() {
// assumes LabelPosition, LabelWidth, LabelHeight are all set if there is a label
labelPosition := label.Position(*edge.Dst.LabelPosition)
labelPosition := label.FromString(*edge.Dst.LabelPosition)
if labelPosition.IsOutside() {
labelWidth := float64(edge.Dst.LabelDimensions.Width)
labelHeight := float64(edge.Dst.LabelDimensions.Height)

View file

@ -435,7 +435,7 @@ func (eid *EdgeID) resolve(m *Map) (_ *EdgeID, _ *Map, common []string, _ error)
}
for len(eid.SrcPath) > 1 && len(eid.DstPath) > 1 {
if !strings.EqualFold(eid.SrcPath[0], eid.DstPath[0]) {
if !strings.EqualFold(eid.SrcPath[0], eid.DstPath[0]) || eid.SrcPath[0] == "*" {
return eid, m, common, nil
}
common = append(common, eid.SrcPath[0])

View file

@ -361,7 +361,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
edge.Route = path
// compile needs to assign edge label positions
if edge.Label.Value != "" {
edge.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
edge.LabelPosition = go2.Pointer(label.InsideMiddleCenter.String())
}
// undo 3d/multiple offset
@ -546,97 +546,27 @@ func inContainer(obj, container *d2graph.Object) *d2graph.Object {
return inContainer(obj.Parent, container)
}
type spacing struct {
top, bottom, left, right float64
}
func getSpacing(obj *d2graph.Object) (margin, padding spacing) {
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
}
labelWidth := float64(obj.LabelDimensions.Width) + 2*label.PADDING
labelHeight := float64(obj.LabelDimensions.Height) + 2*label.PADDING
switch position {
case label.OutsideTopLeft, label.OutsideTopCenter, label.OutsideTopRight:
margin.top = labelHeight
case label.OutsideBottomLeft, label.OutsideBottomCenter, label.OutsideBottomRight:
margin.bottom = labelHeight
case label.OutsideLeftTop, label.OutsideLeftMiddle, label.OutsideLeftBottom:
margin.left = labelWidth
case label.OutsideRightTop, label.OutsideRightMiddle, label.OutsideRightBottom:
margin.right = labelWidth
case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight:
padding.top = labelHeight
case label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight:
padding.bottom = labelHeight
case label.InsideMiddleLeft:
padding.left = labelWidth
case label.InsideMiddleRight:
padding.right = labelWidth
}
}
if obj.Icon != nil && obj.Shape.Value != d2target.ShapeImage {
var position label.Position
if obj.IconPosition != nil {
position = label.Position(*obj.IconPosition)
}
iconSize := float64(d2target.MAX_ICON_SIZE + 2*label.PADDING)
switch position {
case label.OutsideTopLeft, label.OutsideTopCenter, label.OutsideTopRight:
margin.top = math.Max(margin.top, iconSize)
case label.OutsideBottomLeft, label.OutsideBottomCenter, label.OutsideBottomRight:
margin.bottom = math.Max(margin.bottom, iconSize)
case label.OutsideLeftTop, label.OutsideLeftMiddle, label.OutsideLeftBottom:
margin.left = math.Max(margin.left, iconSize)
case label.OutsideRightTop, label.OutsideRightMiddle, label.OutsideRightBottom:
margin.right = math.Max(margin.right, iconSize)
case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight:
padding.top = math.Max(padding.top, iconSize)
case label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight:
padding.bottom = math.Max(padding.bottom, iconSize)
case label.InsideMiddleLeft:
padding.left = math.Max(padding.left, iconSize)
case label.InsideMiddleRight:
padding.right = math.Max(padding.right, iconSize)
}
}
dx, dy := obj.GetModifierElementAdjustments()
margin.right += dx
margin.top += dy
return
}
func positionLabelsIcons(obj *d2graph.Object) {
if obj.Icon != nil && obj.IconPosition == nil {
if len(obj.ChildrenArray) > 0 {
obj.IconPosition = go2.Pointer(string(label.OutsideTopLeft))
obj.IconPosition = go2.Pointer(label.OutsideTopLeft.String())
if obj.LabelPosition == nil {
obj.LabelPosition = go2.Pointer(string(label.OutsideTopRight))
obj.LabelPosition = go2.Pointer(label.OutsideTopRight.String())
return
}
} else {
obj.IconPosition = go2.Pointer(string(label.InsideMiddleCenter))
obj.IconPosition = go2.Pointer(label.InsideMiddleCenter.String())
}
}
if obj.HasLabel() && obj.LabelPosition == nil {
if len(obj.ChildrenArray) > 0 {
obj.LabelPosition = go2.Pointer(string(label.OutsideTopCenter))
obj.LabelPosition = go2.Pointer(label.OutsideTopCenter.String())
} else if obj.HasOutsideBottomLabel() {
obj.LabelPosition = go2.Pointer(string(label.OutsideBottomCenter))
obj.LabelPosition = go2.Pointer(label.OutsideBottomCenter.String())
} else if obj.Icon != nil {
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
obj.LabelPosition = go2.Pointer(label.InsideTopCenter.String())
} else {
obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
obj.LabelPosition = go2.Pointer(label.InsideMiddleCenter.String())
}
}
}
@ -1136,16 +1066,16 @@ func adjustRankSpacing(g *d2graph.Graph, rankSep float64, isHorizontal bool) {
for len(startingParents) > 0 {
var ancestors []*d2graph.Object
for _, parent := range startingParents {
_, padding := getSpacing(parent)
_, padding := parent.Spacing()
if _, has := startingAncestorPositions[parent]; !has {
startingAncestorPositions[parent] = math.Inf(1)
}
var startPosition float64
if isHorizontal {
paddingIncrease := math.Max(0, padding.left-rankSep/2)
paddingIncrease := math.Max(0, padding.Left-rankSep/2)
startPosition = parent.TopLeft.X - paddingIncrease
} else {
paddingIncrease := math.Max(0, padding.top-rankSep/2)
paddingIncrease := math.Max(0, padding.Top-rankSep/2)
startPosition = parent.TopLeft.Y - paddingIncrease
}
startingAncestorPositions[parent] = math.Min(startingAncestorPositions[parent], startPosition)
@ -1159,11 +1089,11 @@ func adjustRankSpacing(g *d2graph.Graph, rankSep float64, isHorizontal bool) {
continue
}
}
margin, _ := getSpacing(child)
margin, _ := child.Spacing()
if isHorizontal {
startPosition = child.TopLeft.X - margin.left - padding.left
startPosition = child.TopLeft.X - margin.Left - padding.Left
} else {
startPosition = child.TopLeft.Y - margin.top - padding.top
startPosition = child.TopLeft.Y - margin.Top - padding.Top
}
startingAncestorPositions[parent] = math.Min(startingAncestorPositions[parent], startPosition)
}
@ -1178,15 +1108,15 @@ func adjustRankSpacing(g *d2graph.Graph, rankSep float64, isHorizontal bool) {
for len(endingParents) > 0 {
var ancestors []*d2graph.Object
for _, parent := range endingParents {
_, padding := getSpacing(parent)
_, padding := parent.Spacing()
if _, has := endingAncestorPositions[parent]; !has {
endingAncestorPositions[parent] = math.Inf(-1)
}
var endPosition float64
if isHorizontal {
endPosition = parent.TopLeft.X + parent.Width + padding.right - rankSep/2.
endPosition = parent.TopLeft.X + parent.Width + padding.Right - rankSep/2.
} else {
endPosition = parent.TopLeft.Y + parent.Height + padding.bottom - rankSep/2.
endPosition = parent.TopLeft.Y + parent.Height + padding.Bottom - rankSep/2.
}
endingAncestorPositions[parent] = math.Max(endingAncestorPositions[parent], endPosition)
@ -1200,12 +1130,12 @@ func adjustRankSpacing(g *d2graph.Graph, rankSep float64, isHorizontal bool) {
continue
}
}
margin, _ := getSpacing(child)
margin, _ := child.Spacing()
if isHorizontal {
endPosition = child.TopLeft.X + child.Width + margin.right + padding.right
endPosition = child.TopLeft.X + child.Width + margin.Right + padding.Right
} else {
endPosition = child.TopLeft.Y + child.Height + margin.bottom + padding.bottom
endPosition = child.TopLeft.Y + child.Height + margin.Bottom + padding.Bottom
}
endingAncestorPositions[parent] = math.Max(endingAncestorPositions[parent], endPosition)
}
@ -1309,60 +1239,60 @@ func adjustCrossRankSpacing(g *d2graph.Graph, rankSep float64, isHorizontal bool
if obj.IsGridDiagram() {
continue
}
margin, padding := getSpacing(obj)
margin, padding := obj.Spacing()
if !isHorizontal {
if prevShift, has := prevMarginBottom[obj]; has {
margin.bottom -= prevShift
margin.Bottom -= prevShift
}
if margin.bottom > 0 {
increased := shiftReachableDown(g, obj, obj.TopLeft.Y+obj.Height, margin.bottom, isHorizontal, true)
if margin.Bottom > 0 {
increased := shiftReachableDown(g, obj, obj.TopLeft.Y+obj.Height, margin.Bottom, isHorizontal, true)
for o := range increased {
prevMarginBottom[o] = math.Max(prevMarginBottom[o], margin.bottom)
prevMarginBottom[o] = math.Max(prevMarginBottom[o], margin.Bottom)
}
}
if padding.bottom > 0 {
shiftReachableDown(g, obj, obj.TopLeft.Y+obj.Height, padding.bottom, isHorizontal, false)
obj.Height += padding.bottom
if padding.Bottom > 0 {
shiftReachableDown(g, obj, obj.TopLeft.Y+obj.Height, padding.Bottom, isHorizontal, false)
obj.Height += padding.Bottom
}
if prevShift, has := prevMarginTop[obj]; has {
margin.top -= prevShift
margin.Top -= prevShift
}
if margin.top > 0 {
increased := shiftReachableDown(g, obj, obj.TopLeft.Y, margin.top, isHorizontal, true)
if margin.Top > 0 {
increased := shiftReachableDown(g, obj, obj.TopLeft.Y, margin.Top, isHorizontal, true)
for o := range increased {
prevMarginTop[o] = math.Max(prevMarginTop[o], margin.top)
prevMarginTop[o] = math.Max(prevMarginTop[o], margin.Top)
}
}
if padding.top > 0 {
shiftReachableDown(g, obj, obj.TopLeft.Y, padding.top, isHorizontal, false)
obj.Height += padding.top
if padding.Top > 0 {
shiftReachableDown(g, obj, obj.TopLeft.Y, padding.Top, isHorizontal, false)
obj.Height += padding.Top
}
} else {
if prevShift, has := prevMarginRight[obj]; has {
margin.right -= prevShift
margin.Right -= prevShift
}
if margin.right > 0 {
increased := shiftReachableDown(g, obj, obj.TopLeft.X+obj.Width, margin.right, isHorizontal, true)
if margin.Right > 0 {
increased := shiftReachableDown(g, obj, obj.TopLeft.X+obj.Width, margin.Right, isHorizontal, true)
for o := range increased {
prevMarginRight[o] = math.Max(prevMarginRight[o], margin.right)
prevMarginRight[o] = math.Max(prevMarginRight[o], margin.Right)
}
}
if padding.right > 0 {
shiftReachableDown(g, obj, obj.TopLeft.X+obj.Width, padding.right, isHorizontal, false)
obj.Width += padding.right
if padding.Right > 0 {
shiftReachableDown(g, obj, obj.TopLeft.X+obj.Width, padding.Right, isHorizontal, false)
obj.Width += padding.Right
}
if prevShift, has := prevMarginLeft[obj]; has {
margin.left -= prevShift
margin.Left -= prevShift
}
if margin.left > 0 {
increased := shiftReachableDown(g, obj, obj.TopLeft.X, margin.left, isHorizontal, true)
if margin.Left > 0 {
increased := shiftReachableDown(g, obj, obj.TopLeft.X, margin.Left, isHorizontal, true)
for o := range increased {
prevMarginLeft[o] = math.Max(prevMarginLeft[o], margin.left)
prevMarginLeft[o] = math.Max(prevMarginLeft[o], margin.Left)
}
}
if padding.left > 0 {
shiftReachableDown(g, obj, obj.TopLeft.X, padding.left, isHorizontal, false)
obj.Width += padding.left
if padding.Left > 0 {
shiftReachableDown(g, obj, obj.TopLeft.X, padding.Left, isHorizontal, false)
obj.Width += padding.Left
}
}
}
@ -1387,11 +1317,11 @@ func fitPadding(obj *d2graph.Object) {
// we will compute a perfectly fit innerBox merging our padding with children's margin,
// but we need to add padding and margin together if an outside child label will overlap with our inside label
_, padding := getSpacing(obj)
padding.top = math.Max(padding.top, DEFAULT_PADDING)
padding.bottom = math.Max(padding.bottom, DEFAULT_PADDING)
padding.left = math.Max(padding.left, DEFAULT_PADDING)
padding.right = math.Max(padding.right, DEFAULT_PADDING)
_, padding := obj.Spacing()
padding.Top = math.Max(padding.Top, DEFAULT_PADDING)
padding.Bottom = math.Max(padding.Bottom, DEFAULT_PADDING)
padding.Left = math.Max(padding.Left, DEFAULT_PADDING)
padding.Right = math.Max(padding.Right, DEFAULT_PADDING)
// where we are (current*) vs where we want to fit each side to (inner*)
currentTop := obj.TopLeft.Y
@ -1408,7 +1338,7 @@ func fitPadding(obj *d2graph.Object) {
var labelPosition, iconPosition label.Position
var labelBox, iconBox *geo.Box
if obj.HasLabel() && obj.LabelPosition != nil {
labelPosition = label.Position(*obj.LabelPosition)
labelPosition = label.FromString(*obj.LabelPosition)
switch labelPosition {
case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight,
label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight,
@ -1420,7 +1350,7 @@ func fitPadding(obj *d2graph.Object) {
}
}
if obj.Icon != nil && shapeType != shape.IMAGE_TYPE && obj.IconPosition != nil {
iconPosition = label.Position(*obj.IconPosition)
iconPosition = label.FromString(*obj.IconPosition)
switch iconPosition {
case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight,
label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight,
@ -1435,14 +1365,14 @@ func fitPadding(obj *d2graph.Object) {
// update the inner positions for children's margin and collect the outside boxes that we cannot overlap with
var innerBoxes []geo.Box
for _, child := range obj.ChildrenArray {
margin, _ := getSpacing(child)
margin, _ := child.Spacing()
dx, dy := child.GetModifierElementAdjustments()
if labelBox != nil || iconBox != nil {
var childLabelBox *geo.Box
var childLabelPosition, childIconPosition label.Position
if child.HasLabel() && child.LabelPosition != nil {
childLabelPosition = label.Position(*child.LabelPosition)
childLabelPosition = label.FromString(*child.LabelPosition)
if childLabelPosition.IsOutside() {
childLabelTL := child.GetLabelTopLeft()
@ -1455,7 +1385,7 @@ func fitPadding(obj *d2graph.Object) {
}
}
if child.Icon != nil && child.Shape.Value != d2target.ShapeImage && child.IconPosition != nil {
childIconPosition = label.Position(*child.IconPosition)
childIconPosition = label.FromString(*child.IconPosition)
if childIconPosition.IsOutside() {
childIconTL := child.GetIconTopLeft()
@ -1465,10 +1395,10 @@ func fitPadding(obj *d2graph.Object) {
}
}
innerTop = math.Min(innerTop, child.TopLeft.Y-dy-math.Max(margin.top, padding.top))
innerBottom = math.Max(innerBottom, child.TopLeft.Y+child.Height+math.Max(margin.bottom, padding.bottom))
innerLeft = math.Min(innerLeft, child.TopLeft.X-math.Max(margin.left, padding.left))
innerRight = math.Max(innerRight, child.TopLeft.X+child.Width+dx+math.Max(margin.right, padding.right))
innerTop = math.Min(innerTop, child.TopLeft.Y-dy-math.Max(margin.Top, padding.Top))
innerBottom = math.Max(innerBottom, child.TopLeft.Y+child.Height+math.Max(margin.Bottom, padding.Bottom))
innerLeft = math.Min(innerLeft, child.TopLeft.X-math.Max(margin.Left, padding.Left))
innerRight = math.Max(innerRight, child.TopLeft.X+child.Width+dx+math.Max(margin.Right, padding.Right))
}
// collect edge label boxes and update inner box for internal edges
@ -1480,7 +1410,7 @@ func fitPadding(obj *d2graph.Object) {
if edge.Label.Value != "" {
labelPosition := label.InsideMiddleCenter
if edge.LabelPosition != nil {
labelPosition = label.Position(*edge.LabelPosition)
labelPosition = label.FromString(*edge.LabelPosition)
}
labelWidth := float64(edge.LabelDimensions.Width)
labelHeight := float64(edge.LabelDimensions.Height)
@ -1490,16 +1420,16 @@ func fitPadding(obj *d2graph.Object) {
innerBoxes = append(innerBoxes, geo.Box{TopLeft: point, Width: labelWidth, Height: labelHeight})
}
innerTop = math.Min(innerTop, point.Y-padding.top)
innerBottom = math.Max(innerBottom, point.Y+labelHeight+padding.bottom)
innerLeft = math.Min(innerLeft, point.X-padding.left)
innerRight = math.Max(innerRight, point.X+labelWidth+padding.right)
innerTop = math.Min(innerTop, point.Y-padding.Top)
innerBottom = math.Max(innerBottom, point.Y+labelHeight+padding.Bottom)
innerLeft = math.Min(innerLeft, point.X-padding.Left)
innerRight = math.Max(innerRight, point.X+labelWidth+padding.Right)
}
for _, point := range edge.Route {
innerTop = math.Min(innerTop, point.Y-padding.top)
innerBottom = math.Max(innerBottom, point.Y+padding.bottom)
innerLeft = math.Min(innerLeft, point.X-padding.left)
innerRight = math.Max(innerRight, point.X+padding.right)
innerTop = math.Min(innerTop, point.Y-padding.Top)
innerBottom = math.Max(innerBottom, point.Y+padding.Bottom)
innerLeft = math.Min(innerLeft, point.X-padding.Left)
innerRight = math.Max(innerRight, point.X+padding.Right)
}
}

View file

@ -360,7 +360,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
continue
}
switch label.Position(*child.IconPosition) {
switch label.FromString(*child.IconPosition) {
case label.OutsideTopLeft, label.OutsideTopCenter, label.OutsideTopRight:
hasTop = true
case label.OutsideBottomLeft, label.OutsideBottomCenter, label.OutsideBottomRight:
@ -605,7 +605,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, opts *ConfigurableOpts) (err
points = points[startIndex : endIndex+1]
if edge.Label.Value != "" {
edge.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
edge.LabelPosition = go2.Pointer(label.InsideMiddleCenter.String())
}
edge.Route = points
@ -1003,7 +1003,7 @@ func adjustPadding(obj *d2graph.Object, width, height float64, padding shapePadd
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) {
switch label.FromString(*obj.LabelPosition) {
case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight:
// Note: for corners we only add height
extraTop = labelHeight
@ -1017,7 +1017,7 @@ func adjustPadding(obj *d2graph.Object, width, height float64, padding shapePadd
}
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) {
switch label.FromString(*obj.IconPosition) {
case label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight:
extraTop = go2.Max(extraTop, iconSize)
case label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight:
@ -1093,7 +1093,7 @@ func adjustDimensions(obj *d2graph.Object) (width, height float64) {
if obj.HasLabel() {
var position label.Position
if obj.LabelPosition != nil {
position = label.Position(*obj.LabelPosition)
position = label.FromString(*obj.LabelPosition)
} else if len(obj.ChildrenArray) == 0 && obj.HasOutsideBottomLabel() {
position = label.OutsideBottomCenter
}
@ -1118,7 +1118,7 @@ func adjustDimensions(obj *d2graph.Object) (width, height float64) {
if obj.Icon != nil && obj.Shape.Value != d2target.ShapeImage {
var position label.Position
if obj.IconPosition != nil {
position = label.Position(*obj.IconPosition)
position = label.FromString(*obj.IconPosition)
}
if position.IsShapePosition() {
@ -1143,7 +1143,7 @@ func adjustDimensions(obj *d2graph.Object) (width, height float64) {
func cleanupAdjustment(obj *d2graph.Object) {
// adjust size and position to account for space reserved for labels
if obj.HasLabel() {
position := label.Position(*obj.LabelPosition)
position := label.FromString(*obj.LabelPosition)
if position.IsShapePosition() {
var labelWidth float64
switch position {
@ -1162,7 +1162,7 @@ func cleanupAdjustment(obj *d2graph.Object) {
}
}
if obj.Icon != nil && obj.Shape.Value != d2target.ShapeImage {
position := label.Position(*obj.IconPosition)
position := label.FromString(*obj.IconPosition)
if position.IsShapePosition() {
var iconWidth float64
switch position {
@ -1202,24 +1202,24 @@ func cleanupAdjustment(obj *d2graph.Object) {
func positionLabelsIcons(obj *d2graph.Object) {
if obj.Icon != nil && obj.IconPosition == nil {
if len(obj.ChildrenArray) > 0 {
obj.IconPosition = go2.Pointer(string(label.InsideTopLeft))
obj.IconPosition = go2.Pointer(label.InsideTopLeft.String())
if obj.LabelPosition == nil {
obj.LabelPosition = go2.Pointer(string(label.InsideTopRight))
obj.LabelPosition = go2.Pointer(label.InsideTopRight.String())
return
}
} else {
obj.IconPosition = go2.Pointer(string(label.InsideMiddleCenter))
obj.IconPosition = go2.Pointer(label.InsideMiddleCenter.String())
}
}
if obj.HasLabel() && obj.LabelPosition == nil {
if len(obj.ChildrenArray) > 0 {
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
obj.LabelPosition = go2.Pointer(label.InsideTopCenter.String())
} else if obj.HasOutsideBottomLabel() {
obj.LabelPosition = go2.Pointer(string(label.OutsideBottomCenter))
obj.LabelPosition = go2.Pointer(label.OutsideBottomCenter.String())
} else if obj.Icon != nil {
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
obj.LabelPosition = go2.Pointer(label.InsideTopCenter.String())
} else {
obj.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
obj.LabelPosition = go2.Pointer(label.InsideMiddleCenter.String())
}
}
}

View file

@ -5,6 +5,7 @@ import (
"context"
"fmt"
"math"
"strconv"
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2target"
@ -30,6 +31,13 @@ func Layout(ctx context.Context, g *d2graph.Graph) error {
return err
}
if obj.HasLabel() && obj.LabelPosition == nil {
obj.LabelPosition = go2.Pointer(label.InsideTopCenter.String())
}
if obj.Icon != nil && obj.IconPosition == nil {
obj.IconPosition = go2.Pointer(label.InsideTopLeft.String())
}
if obj.Box != nil {
// CONTAINER_PADDING is default, but use gap value if set
horizontalPadding, verticalPadding := CONTAINER_PADDING, CONTAINER_PADDING
@ -40,62 +48,136 @@ func Layout(ctx context.Context, g *d2graph.Graph) error {
verticalPadding = gd.verticalGap
}
// size shape according to grid
obj.SizeToContent(gd.width, gd.height, float64(2*horizontalPadding), float64(2*verticalPadding))
contentWidth, contentHeight := gd.width, gd.height
// compute where the grid should be placed inside shape
s := obj.ToShape()
innerBox := s.GetInnerBox()
if innerBox.TopLeft.X != 0 || innerBox.TopLeft.Y != 0 {
gd.shift(innerBox.TopLeft.X, innerBox.TopLeft.Y)
var labelPosition, iconPosition label.Position
if obj.LabelPosition != nil {
labelPosition = label.FromString(*obj.LabelPosition)
}
if obj.IconPosition != nil {
iconPosition = label.FromString(*obj.IconPosition)
}
// compute how much space the label and icon occupy
var occupiedWidth, occupiedHeight float64
if obj.Icon != nil {
iconSpace := float64(d2target.MAX_ICON_SIZE + 2*label.PADDING)
occupiedWidth = iconSpace
occupiedHeight = iconSpace
_, padding := obj.Spacing()
var labelWidth, labelHeight float64
if obj.LabelDimensions.Width > 0 {
labelWidth = float64(obj.LabelDimensions.Width) + 2*label.PADDING
}
if obj.LabelDimensions.Height > 0 {
labelHeight = float64(obj.LabelDimensions.Height) + 2*label.PADDING
}
var dx, dy float64
if obj.LabelDimensions.Height != 0 {
occupiedHeight = math.Max(
occupiedHeight,
float64(obj.LabelDimensions.Height)+2*label.PADDING,
)
if labelWidth > 0 {
switch labelPosition {
case label.OutsideTopLeft, label.OutsideTopCenter, label.OutsideTopRight,
label.InsideTopLeft, label.InsideTopCenter, label.InsideTopRight,
label.InsideBottomLeft, label.InsideBottomCenter, label.InsideBottomRight,
label.OutsideBottomLeft, label.OutsideBottomCenter, label.OutsideBottomRight:
overflow := labelWidth - contentWidth
if overflow > 0 {
padding.Left += overflow / 2
padding.Right += overflow / 2
}
}
}
if obj.LabelDimensions.Width != 0 {
// . ├────┤───────├────┤
// . icon label icon
// with an icon in top left we need 2x the space to fit the label in the center
occupiedWidth *= 2
occupiedWidth += float64(obj.LabelDimensions.Width) + 2*label.PADDING
if occupiedWidth > obj.Width {
dx = (occupiedWidth - obj.Width) / 2
obj.Width = occupiedWidth
if labelHeight > 0 {
switch labelPosition {
case label.OutsideLeftTop, label.OutsideLeftMiddle, label.OutsideLeftBottom,
label.InsideMiddleLeft, label.InsideMiddleCenter, label.InsideMiddleRight,
label.OutsideRightTop, label.OutsideRightMiddle, label.OutsideRightBottom:
overflow := labelHeight - contentHeight
if overflow > 0 {
padding.Top += overflow / 2
padding.Bottom += overflow / 2
}
}
}
if occupiedHeight > float64(verticalPadding) {
// if the label doesn't fit within the padding, we need to add more
dy = occupiedHeight - float64(verticalPadding)
obj.Height += dy
// configure spacing for default label+icon
if iconPosition == label.InsideTopLeft && labelPosition == label.InsideTopCenter {
// . ├────┤───────├────┤
// . icon label icon
// with an icon in top left we need 2x the space to fit the label in the center
iconSize := float64(d2target.MAX_ICON_SIZE) + 2*label.PADDING
padding.Left = math.Max(padding.Left, iconSize)
padding.Right = math.Max(padding.Right, iconSize)
minWidth := 2*iconSize + float64(obj.LabelDimensions.Width) + 2*label.PADDING
overflow := minWidth - contentWidth
if overflow > 0 {
padding.Left = math.Max(padding.Left, overflow/2)
padding.Right = math.Max(padding.Right, overflow/2)
}
}
// we need to center children if we have to expand to fit the container label
overflowTop := padding.Top - float64(verticalPadding)
if overflowTop > 0 {
contentHeight += overflowTop
dy += overflowTop
}
overflowBottom := padding.Bottom - float64(verticalPadding)
if overflowBottom > 0 {
contentHeight += overflowBottom
}
overflowLeft := padding.Left - float64(horizontalPadding)
if overflowLeft > 0 {
contentWidth += overflowLeft
dx += overflowLeft
}
overflowRight := padding.Right - float64(horizontalPadding)
if overflowRight > 0 {
contentWidth += overflowRight
}
// manually handle desiredWidth/Height so we can center the grid
var desiredWidth, desiredHeight int
var originalWidthAttr, originalHeightAttr *d2graph.Scalar
if obj.WidthAttr != nil {
desiredWidth, _ = strconv.Atoi(obj.WidthAttr.Value)
// SizeToContent without desired width
originalWidthAttr = obj.WidthAttr
obj.WidthAttr = nil
}
if obj.HeightAttr != nil {
desiredHeight, _ = strconv.Atoi(obj.HeightAttr.Value)
originalHeightAttr = obj.HeightAttr
obj.HeightAttr = nil
}
// size shape according to grid
obj.SizeToContent(contentWidth, contentHeight, float64(2*horizontalPadding), float64(2*verticalPadding))
if originalWidthAttr != nil {
obj.WidthAttr = originalWidthAttr
}
if originalHeightAttr != nil {
obj.HeightAttr = originalHeightAttr
}
if desiredWidth > 0 {
ddx := float64(desiredWidth) - obj.Width
if ddx > 0 {
dx += ddx / 2
obj.Width = float64(desiredWidth)
}
}
if desiredHeight > 0 {
ddy := float64(desiredHeight) - obj.Height
if ddy > 0 {
dy += ddy / 2
obj.Height = float64(desiredHeight)
}
}
// compute where the grid should be placed inside shape
innerBox := obj.ToShape().GetInnerBox()
dx = innerBox.TopLeft.X + dx
dy = innerBox.TopLeft.Y + dy
if dx != 0 || dy != 0 {
gd.shift(dx, dy)
}
}
if obj.HasLabel() {
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
}
if obj.Icon != nil {
obj.IconPosition = go2.Pointer(string(label.InsideTopLeft))
}
// simple straight line edge routing between grid objects
for _, e := range g.Edges {
if !e.Src.Parent.IsDescendantOf(obj) && !e.Dst.Parent.IsDescendantOf(obj) {
@ -111,7 +193,7 @@ func Layout(ctx context.Context, g *d2graph.Graph) error {
e.Route = []*geo.Point{e.Src.Center(), e.Dst.Center()}
e.TraceToShape(e.Route, 0, 1)
if e.Label.Value != "" {
e.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
e.LabelPosition = go2.Pointer(label.InsideMiddleCenter.String())
}
}
@ -119,8 +201,6 @@ func Layout(ctx context.Context, g *d2graph.Graph) error {
g.Root.TopLeft = geo.NewPoint(0, 0)
}
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
if g.RootLevel > 0 {
horizontalPadding, verticalPadding := CONTAINER_PADDING, CONTAINER_PADDING
if obj.GridGap != nil || obj.HorizontalGap != nil {
@ -147,25 +227,25 @@ func layoutGrid(g *d2graph.Graph, obj *d2graph.Object) (*gridDiagram, error) {
positionedLabel := false
if o.Icon != nil && o.IconPosition == nil {
if len(o.ChildrenArray) > 0 {
o.IconPosition = go2.Pointer(string(label.OutsideTopLeft))
o.IconPosition = go2.Pointer(label.OutsideTopLeft.String())
// don't overwrite position if nested graph layout positioned label/icon
if o.LabelPosition == nil {
o.LabelPosition = go2.Pointer(string(label.OutsideTopRight))
o.LabelPosition = go2.Pointer(label.OutsideTopRight.String())
positionedLabel = true
}
} else {
o.IconPosition = go2.Pointer(string(label.InsideMiddleCenter))
o.IconPosition = go2.Pointer(label.InsideMiddleCenter.String())
}
}
if !positionedLabel && o.HasLabel() && o.LabelPosition == nil {
if len(o.ChildrenArray) > 0 {
o.LabelPosition = go2.Pointer(string(label.OutsideTopCenter))
o.LabelPosition = go2.Pointer(label.OutsideTopCenter.String())
} else if o.HasOutsideBottomLabel() {
o.LabelPosition = go2.Pointer(string(label.OutsideBottomCenter))
o.LabelPosition = go2.Pointer(label.OutsideBottomCenter.String())
} else if o.Icon != nil {
o.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
o.LabelPosition = go2.Pointer(label.InsideTopCenter.String())
} else {
o.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
o.LabelPosition = go2.Pointer(label.InsideMiddleCenter.String())
}
}
}

View file

@ -103,35 +103,63 @@ func LayoutNested(ctx context.Context, g *d2graph.Graph, graphInfo GraphInfo, co
if isGridCellContainer && gi.isDefault() {
// if we are in a grid diagram, and our children have descendants
// we need to run layout on them first, even if they are not special diagram types
// First we extract the grid cell container as a nested graph with includeSelf=true
// resulting in externalEdges=[A, C] and nestedGraph.Edges=[B]
// ┌grid(g.Root)───────────────────┐ ┌grid(g.Root)───────────────────┐
// │ ┌────┐ ┌curr───────────┐ │ │ ┌────┐ │
// │ │ │ │ ┌──┐ ┌──┐ │ │ │ │ │ │
// │ │ ├──A──►│ │ ├─B─►│ │ │ │ => │ │ │ │
// │ │ ├──────┼C─┼──┼───►│ │ │ │ │ │ │ │
// │ │ │ │ └──┘ └──┘ │ │ │ │ │ │
// │ └────┘ └───────────────┘ │ │ └────┘ │
// └───────────────────────────────┘ └───────────────────────────────┘
nestedGraph, externalEdges := ExtractSubgraph(curr, true)
// Then we layout curr as a nested graph and re-inject it
id := curr.AbsID()
err := LayoutNested(ctx, nestedGraph, GraphInfo{}, coreLayout)
if err != nil {
return err
}
InjectNested(g.Root, nestedGraph, false)
g.Edges = append(g.Edges, externalEdges...)
restoreOrder()
// need to update curr *Object incase layout changed it
var obj *d2graph.Object
// layout can replace Objects so we need to update the references we are holding onto (curr + externalEdges)
idToObj := make(map[string]*d2graph.Object)
for _, o := range g.Objects {
if o.AbsID() == id {
obj = o
break
idToObj[o.AbsID()] = o
}
lookup := func(idStr string) (*d2graph.Object, error) {
o, exists := idToObj[idStr]
if !exists {
return nil, fmt.Errorf("could not find object %#v after layout", idStr)
}
return o, nil
}
if obj == nil {
return fmt.Errorf("could not find object %#v after layout", id)
curr, err = lookup(id)
if err != nil {
return err
}
for _, e := range externalEdges {
src, err := lookup(e.Src.AbsID())
if err != nil {
return err
}
e.Src = src
dst, err := lookup(e.Dst.AbsID())
if err != nil {
return err
}
e.Dst = dst
}
curr = obj
// position nested graph (excluding curr) relative to curr
dx := 0 - curr.TopLeft.X
dy := 0 - curr.TopLeft.Y
for _, o := range nestedGraph.Objects {
if o.AbsID() == curr.AbsID() {
if o == curr {
continue
}
o.TopLeft.X += dx
@ -141,7 +169,19 @@ func LayoutNested(ctx context.Context, g *d2graph.Graph, graphInfo GraphInfo, co
e.Move(dx, dy)
}
// now we keep the descendants out until after grid layout
// Then after re-injecting everything, we extract curr with includeSelf=false,
// and externalEdges=[C], nestedGraph.Edges=[B], and graph.Edges=[A].
// This will leave A in the graph to be routed by grid layout, and C will have cross-graph edge routing
// Note: currently grid layout's cell-cell edge routing, and cross-graph edge routing behave the same,
// but these are simple placeholder routings and they may be different in the future
// ┌grid(g.Root)───────────────────┐ ┌grid(g.Root)───────────────────┐
// │ ┌────┐ ┌curr───────────┐ │ │ ┌────┐ ┌curr───────────┐ │
// │ │ │ │ ┌──┐ ┌──┐ │ │ │ │ │ │ │ │
// │ │ ├──A──►│ │ ├─B─►│ │ │ │ => │ │ ├──A──►│ │ │
// │ │ ├──────┼C─┼──┼───►│ │ │ │ │ │ │ │ │ │
// │ │ │ │ └──┘ └──┘ │ │ │ │ │ │ │ │
// │ └────┘ └───────────────┘ │ │ └────┘ └───────────────┘ │
// └───────────────────────────────┘ └───────────────────────────────┘
nestedGraph, externalEdges = ExtractSubgraph(curr, false)
extractedEdges = append(extractedEdges, externalEdges...)
@ -275,7 +315,7 @@ func LayoutNested(ctx context.Context, g *d2graph.Graph, graphInfo GraphInfo, co
e.Route = []*geo.Point{e.Src.Center(), e.Dst.Center()}
e.TraceToShape(e.Route, 0, 1)
if e.Label.Value != "" {
e.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
e.LabelPosition = go2.Pointer(label.InsideMiddleCenter.String())
}
}
@ -305,6 +345,8 @@ func ExtractSubgraph(container *d2graph.Object, includeSelf bool) (nestedGraph *
}
nestedGraph.Root.Attributes = container.Attributes
nestedGraph.Root.Box = &geo.Box{}
nestedGraph.Root.LabelPosition = container.LabelPosition
nestedGraph.Root.IconPosition = container.IconPosition
isNestedObject := func(obj *d2graph.Object) bool {
if includeSelf {

View file

@ -178,7 +178,7 @@ func boundingBox(g *d2graph.Graph) (tl, br *geo.Point) {
x2 = math.Max(x2, obj.TopLeft.X+obj.Width)
y2 = math.Max(y2, obj.TopLeft.Y+obj.Height)
if obj.Label.Value != "" && obj.LabelPosition != nil {
labelPosition := label.Position(*obj.LabelPosition)
labelPosition := label.FromString(*obj.LabelPosition)
if labelPosition.IsOutside() {
labelTL := labelPosition.GetPointOnBox(obj.Box, label.PADDING, float64(obj.LabelDimensions.Width), float64(obj.LabelDimensions.Height))
x1 = math.Min(x1, labelTL.X)

View file

@ -32,7 +32,7 @@ func Layout(ctx context.Context, g *d2graph.Graph, layout d2graph.LayoutGraph) e
obj := g.Root
obj.LabelPosition = go2.Pointer(string(label.InsideTopCenter))
obj.LabelPosition = go2.Pointer(label.InsideTopCenter.String())
// shift the sequence diagrams as they are always placed at (0, 0) with some padding
sd.shift(

View file

@ -141,12 +141,12 @@ n2 -> n1
}
// check label positions
if *g.Edges[0].LabelPosition != string(label.InsideMiddleCenter) {
t.Fatalf("expected edge label to be placed on %s, got %s", string(label.InsideMiddleCenter), *g.Edges[0].LabelPosition)
if *g.Edges[0].LabelPosition != label.InsideMiddleCenter.String() {
t.Fatalf("expected edge label to be placed on %s, got %s", label.InsideMiddleCenter, *g.Edges[0].LabelPosition)
}
if *g.Edges[1].LabelPosition != string(label.InsideMiddleCenter) {
t.Fatalf("expected edge label to be placed on %s, got %s", string(label.InsideMiddleCenter), *g.Edges[0].LabelPosition)
if *g.Edges[1].LabelPosition != label.InsideMiddleCenter.String() {
t.Fatalf("expected edge label to be placed on %s, got %s", label.InsideMiddleCenter, *g.Edges[0].LabelPosition)
}
}

View file

@ -76,7 +76,7 @@ func newSequenceDiagram(objects []*d2graph.Object, messages []*d2graph.Edge) (*s
// Groups may have more nested groups
for len(queue) > 0 {
curr := queue[0]
curr.LabelPosition = go2.Pointer(string(label.InsideTopLeft))
curr.LabelPosition = go2.Pointer(label.InsideTopLeft.String())
groups = append(groups, curr)
queue = queue[1:]
queue = append(queue, curr.ChildrenArray...)
@ -135,7 +135,7 @@ func newSequenceDiagram(objects []*d2graph.Object, messages []*d2graph.Edge) (*s
child.Shape = d2graph.Scalar{Value: shape.PAGE_TYPE}
sd.notes = append(sd.notes, child)
sd.objectRank[child] = rank
child.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
child.LabelPosition = go2.Pointer(label.InsideMiddleCenter.String())
maxNoteWidth = math.Max(maxNoteWidth, child.Width)
} else {
// spans have no labels
@ -338,13 +338,13 @@ func (sd *sequenceDiagram) placeActors() {
for rank, actor := range sd.actors {
var yOffset float64
if actor.HasOutsideBottomLabel() {
actor.LabelPosition = go2.Pointer(string(label.OutsideBottomCenter))
actor.LabelPosition = go2.Pointer(label.OutsideBottomCenter.String())
yOffset = sd.maxActorHeight - actor.Height
if actor.HasLabel() {
yOffset -= float64(actor.LabelDimensions.Height)
}
} else {
actor.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
actor.LabelPosition = go2.Pointer(label.InsideMiddleCenter.String())
yOffset = sd.maxActorHeight - actor.Height
}
halfWidth := actor.Width / 2.
@ -382,7 +382,7 @@ func (sd *sequenceDiagram) addLifelineEdges() {
for _, actor := range sd.actors {
actorBottom := actor.Center()
actorBottom.Y = actor.TopLeft.Y + actor.Height
if *actor.LabelPosition == string(label.OutsideBottomCenter) && actor.HasLabel() {
if *actor.LabelPosition == label.OutsideBottomCenter.String() && actor.HasLabel() {
actorBottom.Y += float64(actor.LabelDimensions.Height) + LIFELINE_LABEL_PAD
}
actorLifelineEnd := actor.Center()
@ -602,7 +602,7 @@ func (sd *sequenceDiagram) routeMessages() error {
messageOffset += sd.yStep
if message.Label.Value != "" {
message.LabelPosition = go2.Pointer(string(label.InsideMiddleCenter))
message.LabelPosition = go2.Pointer(label.InsideMiddleCenter.String())
}
}
return nil

View file

@ -61,7 +61,7 @@ func (p *dagrePlugin) HydrateOpts(opts []byte) error {
func (p *dagrePlugin) Info(ctx context.Context) (*PluginInfo, error) {
p.mu.Lock()
defer p.mu.Unlock()
opts := xmain.NewOpts(nil, nil, nil)
opts := xmain.NewOpts(nil, nil)
flags, err := p.Flags(ctx)
if err != nil {
return nil, err

View file

@ -76,7 +76,7 @@ func (p *elkPlugin) HydrateOpts(opts []byte) error {
}
func (p elkPlugin) Info(ctx context.Context) (*PluginInfo, error) {
opts := xmain.NewOpts(nil, nil, nil)
opts := xmain.NewOpts(nil, nil)
flags, err := p.Flags(ctx)
if err != nil {
return nil, err

View file

@ -38,7 +38,8 @@ func FeatureSupportCheck(info *PluginInfo, g *d2graph.Graph) error {
return fmt.Errorf(`Object "%s" has attribute "top" and/or "left" set, but layout engine "%s" does not support locked positions. See https://d2lang.com/tour/layouts/#layout-specific-functionality for more.`, obj.AbsID(), info.Name)
}
}
if (obj.WidthAttr != nil || obj.HeightAttr != nil) && len(obj.ChildrenArray) > 0 {
if (obj.WidthAttr != nil || obj.HeightAttr != nil) &&
len(obj.ChildrenArray) > 0 && !obj.IsGridDiagram() {
if _, ok := featureMap[CONTAINER_DIMENSIONS]; !ok {
return fmt.Errorf(`Object "%s" has attribute "width" and/or "height" set, but layout engine "%s" does not support dimensions set on containers. See https://d2lang.com/tour/layouts/#layout-specific-functionality for more.`, obj.AbsID(), info.Name)
}

View file

@ -7,6 +7,7 @@ package appendix
import (
"fmt"
"regexp"
"sort"
"strconv"
"strings"
@ -146,19 +147,48 @@ func Append(diagram *d2target.Diagram, ruler *textmeasure.Ruler, in []byte) []by
closingIndex := strings.LastIndex(svg, "</svg></svg>")
svg = svg[:closingIndex] + appendix + svg[closingIndex:]
// icons are numbered according to diagram.Shapes which is based on their order of definition,
// but they appear in the svg according to renderOrder so we have to replace in that order
type appendixIcon struct {
number int
isTooltip bool
shape d2target.Shape
}
var renderOrder []appendixIcon
i := 1
for _, s := range diagram.Shapes {
if s.Tooltip != "" {
// The clip-path has a unique ID, so this won't replace any user icons
// In the existing SVG, the transform places it top-left, so we adjust
svg = strings.Replace(svg, d2svg.TooltipIcon, generateNumberedIcon(i, 0, ICON_RADIUS), 1)
renderOrder = append(renderOrder, appendixIcon{i, true, s})
i++
}
if s.Link != "" {
svg = strings.Replace(svg, d2svg.LinkIcon, generateNumberedIcon(i, 0, ICON_RADIUS), 1)
renderOrder = append(renderOrder, appendixIcon{i, false, s})
i++
}
}
// sort to match render order
sort.SliceStable(renderOrder, func(i, j int) bool {
iZIndex := renderOrder[i].shape.GetZIndex()
jZIndex := renderOrder[j].shape.GetZIndex()
if iZIndex != jZIndex {
return iZIndex < jZIndex
}
return renderOrder[i].shape.Level < renderOrder[j].shape.Level
})
// replace each rendered svg icon
for _, icon := range renderOrder {
// The clip-path has a unique ID, so this won't replace any user icons
// In the existing SVG, the transform places it top-left, so we adjust
var iconStr string
if icon.isTooltip {
iconStr = d2svg.TooltipIcon
} else {
iconStr = d2svg.LinkIcon
}
svg = strings.Replace(svg, iconStr, generateNumberedIcon(icon.number, 0, ICON_RADIUS), 1)
}
return []byte(svg)
}

View file

@ -138,6 +138,29 @@ func arrowheadMarker(isTarget bool, id string, connection d2target.Connection) s
)
}
path = polygonEl.Render()
case d2target.UnfilledTriangleArrowhead:
polygonEl := d2themes.NewThemableElement("polygon")
polygonEl.Fill = d2target.BG_COLOR
polygonEl.Stroke = connection.Stroke
polygonEl.ClassName = "connection"
polygonEl.Attributes = fmt.Sprintf(`stroke-width="%d"`, connection.StrokeWidth)
inset := strokeWidth / 2
if isTarget {
polygonEl.Points = fmt.Sprintf("%f,%f %f,%f %f,%f",
inset, inset,
width-inset, height/2.0,
inset, height-inset,
)
} else {
polygonEl.Points = fmt.Sprintf("%f,%f %f,%f %f,%f",
width-inset, inset,
inset, height/2.0,
width-inset, height-inset,
)
}
path = polygonEl.Render()
case d2target.TriangleArrowhead:
polygonEl := d2themes.NewThemableElement("polygon")
polygonEl.Fill = connection.Stroke
@ -514,7 +537,7 @@ func drawConnection(writer io.Writer, labelMaskID string, connection d2target.Co
labelTL.X = math.Round(labelTL.X)
labelTL.Y = math.Round(labelTL.Y)
if label.Position(connection.LabelPosition).IsOnEdge() {
if label.FromString(connection.LabelPosition).IsOnEdge() {
labelMask = makeLabelMask(labelTL, connection.LabelWidth, connection.LabelHeight, 1)
} else {
labelMask = makeLabelMask(labelTL, connection.LabelWidth, connection.LabelHeight, 0.75)
@ -1178,7 +1201,7 @@ func drawShape(writer, appendixWriter io.Writer, diagramHash string, targetShape
fmt.Fprint(writer, `</g>`)
if targetShape.Icon != nil && targetShape.Type != d2target.ShapeImage {
iconPosition := label.Position(targetShape.IconPosition)
iconPosition := label.FromString(targetShape.IconPosition)
var box *geo.Box
if iconPosition.IsOutside() {
box = s.GetBox()
@ -1199,7 +1222,7 @@ func drawShape(writer, appendixWriter io.Writer, diagramHash string, targetShape
}
if targetShape.Label != "" {
labelPosition := label.Position(targetShape.LabelPosition)
labelPosition := label.FromString(targetShape.LabelPosition)
var box *geo.Box
if labelPosition.IsOutside() {
box = s.GetBox().Copy()

View file

@ -74,7 +74,8 @@ type Diagram struct {
}
// boardPath comes in the form of "x/layers/z/scenarios/a"
// or in the form of "layers/z/scenarios/a"
// or "layers/z/scenarios/a"
// or "x/z/a"
func (d *Diagram) GetBoard(boardPath string) *Diagram {
path := strings.Split(boardPath, string(os.PathSeparator))
if len(path) == 0 || len(boardPath) == 0 {
@ -127,17 +128,17 @@ func (d *Diagram) getBoard(boardPath []string) *Diagram {
for _, b := range d.Layers {
if b.Name == head {
return b.getBoard(boardPath[2:])
return b.getBoard(boardPath[1:])
}
}
for _, b := range d.Scenarios {
if b.Name == head {
return b.getBoard(boardPath[2:])
return b.getBoard(boardPath[1:])
}
}
for _, b := range d.Steps {
if b.Name == head {
return b.getBoard(boardPath[2:])
return b.getBoard(boardPath[1:])
}
}
return nil
@ -290,7 +291,7 @@ func (diagram Diagram) BoundingBox() (topLeft, bottomRight Point) {
x2 = go2.Max(x2, targetShape.Pos.X+MULTIPLE_OFFSET+targetShape.Width+targetShape.StrokeWidth)
}
if targetShape.Icon != nil && label.Position(targetShape.IconPosition).IsOutside() {
if targetShape.Icon != nil && label.FromString(targetShape.IconPosition).IsOutside() {
contentBox := geo.NewBox(geo.NewPoint(0, 0), float64(targetShape.Width), float64(targetShape.Height))
s := shape.NewShape(targetShape.Type, contentBox)
size := GetIconSize(s.GetInnerBox(), targetShape.IconPosition)
@ -307,7 +308,7 @@ func (diagram Diagram) BoundingBox() (topLeft, bottomRight Point) {
}
if targetShape.Label != "" {
labelPosition := label.Position(targetShape.LabelPosition)
labelPosition := label.FromString(targetShape.LabelPosition)
if !labelPosition.IsOutside() {
continue
}
@ -636,7 +637,7 @@ func (c Connection) CSSStyle() string {
}
func (c *Connection) GetLabelTopLeft() *geo.Point {
point, _ := label.Position(c.LabelPosition).GetPointOnRoute(
point, _ := label.FromString(c.LabelPosition).GetPointOnRoute(
c.Route,
float64(c.StrokeWidth),
c.LabelPercentage,
@ -719,13 +720,14 @@ func (c Connection) GetID() string {
type Arrowhead string
const (
NoArrowhead Arrowhead = "none"
ArrowArrowhead Arrowhead = "arrow"
TriangleArrowhead Arrowhead = "triangle"
DiamondArrowhead Arrowhead = "diamond"
FilledDiamondArrowhead Arrowhead = "filled-diamond"
CircleArrowhead Arrowhead = "circle"
FilledCircleArrowhead Arrowhead = "filled-circle"
NoArrowhead Arrowhead = "none"
ArrowArrowhead Arrowhead = "arrow"
UnfilledTriangleArrowhead Arrowhead = "unfilled-triangle"
TriangleArrowhead Arrowhead = "triangle"
DiamondArrowhead Arrowhead = "diamond"
FilledDiamondArrowhead Arrowhead = "filled-diamond"
CircleArrowhead Arrowhead = "circle"
FilledCircleArrowhead Arrowhead = "filled-circle"
// For fat arrows
LineArrowhead Arrowhead = "line"
@ -739,29 +741,28 @@ const (
DefaultArrowhead Arrowhead = TriangleArrowhead
)
// valid values for arrowhead.shape
var Arrowheads = map[string]struct{}{
string(NoArrowhead): {},
string(ArrowArrowhead): {},
string(TriangleArrowhead): {},
string(DiamondArrowhead): {},
string(FilledDiamondArrowhead): {},
string(CircleArrowhead): {},
string(FilledCircleArrowhead): {},
string(CfOne): {},
string(CfMany): {},
string(CfOneRequired): {},
string(CfManyRequired): {},
string(NoArrowhead): {},
string(ArrowArrowhead): {},
string(TriangleArrowhead): {},
string(DiamondArrowhead): {},
string(CircleArrowhead): {},
string(CfOne): {},
string(CfMany): {},
string(CfOneRequired): {},
string(CfManyRequired): {},
}
func ToArrowhead(arrowheadType string, filled bool) Arrowhead {
func ToArrowhead(arrowheadType string, filled *bool) Arrowhead {
switch arrowheadType {
case string(DiamondArrowhead):
if filled {
if filled != nil && *filled {
return FilledDiamondArrowhead
}
return DiamondArrowhead
case string(CircleArrowhead):
if filled {
if filled != nil && *filled {
return FilledCircleArrowhead
}
return CircleArrowhead
@ -770,6 +771,9 @@ func ToArrowhead(arrowheadType string, filled bool) Arrowhead {
case string(ArrowArrowhead):
return ArrowArrowhead
case string(TriangleArrowhead):
if filled != nil && !(*filled) {
return UnfilledTriangleArrowhead
}
return TriangleArrowhead
case string(CfOne):
return CfOne
@ -780,6 +784,10 @@ func ToArrowhead(arrowheadType string, filled bool) Arrowhead {
case string(CfManyRequired):
return CfManyRequired
default:
if DefaultArrowhead == TriangleArrowhead &&
filled != nil && !(*filled) {
return UnfilledTriangleArrowhead
}
return DefaultArrowhead
}
}
@ -798,6 +806,11 @@ func (arrowhead Arrowhead) Dimensions(strokeWidth float64) (width, height float6
baseHeight = 4
widthMultiplier = 3
heightMultiplier = 4
case UnfilledTriangleArrowhead:
baseWidth = 7
baseHeight = 7
widthMultiplier = 3
heightMultiplier = 4
case LineArrowhead:
widthMultiplier = 5
heightMultiplier = 8
@ -967,7 +980,7 @@ func init() {
}
func GetIconSize(box *geo.Box, position string) int {
iconPosition := label.Position(position)
iconPosition := label.FromString(position)
minDimension := int(math.Min(box.Width, box.Height))
halfMinDimension := int(math.Ceil(0.5 * float64(minDimension)))

View file

@ -3,12 +3,18 @@ package e2etests_cli
import (
"bytes"
"context"
"errors"
"fmt"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"testing"
"time"
"nhooyr.io/websocket"
"oss.terrastruct.com/util-go/assert"
"oss.terrastruct.com/util-go/diff"
"oss.terrastruct.com/util-go/xmain"
@ -544,6 +550,235 @@ i used to read
assert.Equal(t, "x -> y\n", string(gotBar))
},
},
{
name: "watch-regular",
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
writeFile(t, dir, "index.d2", `
a -> b
b.link: layers.cream
layers: {
cream: {
c -> b
}
}`)
stderr := &bytes.Buffer{}
tms := testMain(dir, env, "--watch", "--browser=0", "index.d2")
tms.Stderr = stderr
tms.Start(t, ctx)
defer func() {
// Manually close, since watcher is daemon
err := tms.Signal(ctx, os.Interrupt)
assert.Success(t, err)
}()
// Wait for watch server to spin up and listen
urlRE := regexp.MustCompile(`127.0.0.1:([0-9]+)`)
watchURL, err := waitLogs(ctx, stderr, urlRE)
assert.Success(t, err)
stderr.Reset()
// Start a client
c, _, err := websocket.Dial(ctx, fmt.Sprintf("ws://%s/watch", watchURL), nil)
assert.Success(t, err)
defer c.CloseNow()
// Get the link
_, msg, err := c.Read(ctx)
assert.Success(t, err)
aRE := regexp.MustCompile(`href=\\"([^\"]*)\\"`)
match := aRE.FindSubmatch(msg)
assert.Equal(t, 2, len(match))
linkedPath := match[1]
err = getWatchPage(ctx, t, fmt.Sprintf("http://%s/%s", watchURL, linkedPath))
assert.Success(t, err)
successRE := regexp.MustCompile(`broadcasting update to 1 client`)
_, err = waitLogs(ctx, stderr, successRE)
assert.Success(t, err)
},
},
{
name: "watch-ok-link",
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
// This link technically works because D2 interprets it as a URL,
// and on local filesystem, that is whe path where the compilation happens
// to output it to.
writeFile(t, dir, "index.d2", `
a -> b
b.link: cream
layers: {
cream: {
c -> b
}
}`)
stderr := &bytes.Buffer{}
tms := testMain(dir, env, "--watch", "--browser=0", "index.d2")
tms.Stderr = stderr
tms.Start(t, ctx)
defer func() {
// Manually close, since watcher is daemon
err := tms.Signal(ctx, os.Interrupt)
assert.Success(t, err)
}()
// Wait for watch server to spin up and listen
urlRE := regexp.MustCompile(`127.0.0.1:([0-9]+)`)
watchURL, err := waitLogs(ctx, stderr, urlRE)
assert.Success(t, err)
stderr.Reset()
// Start a client
c, _, err := websocket.Dial(ctx, fmt.Sprintf("ws://%s/watch", watchURL), nil)
assert.Success(t, err)
defer c.CloseNow()
// Get the link
_, msg, err := c.Read(ctx)
assert.Success(t, err)
aRE := regexp.MustCompile(`href=\\"([^\"]*)\\"`)
match := aRE.FindSubmatch(msg)
assert.Equal(t, 2, len(match))
linkedPath := match[1]
err = getWatchPage(ctx, t, fmt.Sprintf("http://%s/%s", watchURL, linkedPath))
assert.Success(t, err)
successRE := regexp.MustCompile(`broadcasting update to 1 client`)
_, err = waitLogs(ctx, stderr, successRE)
assert.Success(t, err)
},
},
{
name: "watch-bad-link",
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
// Just verify we don't crash even with a bad link (it's treated as a URL, which users might have locally)
writeFile(t, dir, "index.d2", `
a -> b
b.link: dream
layers: {
cream: {
c -> b
}
}`)
stderr := &bytes.Buffer{}
tms := testMain(dir, env, "--watch", "--browser=0", "index.d2")
tms.Stderr = stderr
tms.Start(t, ctx)
defer func() {
// Manually close, since watcher is daemon
err := tms.Signal(ctx, os.Interrupt)
assert.Success(t, err)
}()
// Wait for watch server to spin up and listen
urlRE := regexp.MustCompile(`127.0.0.1:([0-9]+)`)
watchURL, err := waitLogs(ctx, stderr, urlRE)
assert.Success(t, err)
stderr.Reset()
// Start a client
c, _, err := websocket.Dial(ctx, fmt.Sprintf("ws://%s/watch", watchURL), nil)
assert.Success(t, err)
defer c.CloseNow()
// Get the link
_, msg, err := c.Read(ctx)
assert.Success(t, err)
aRE := regexp.MustCompile(`href=\\"([^\"]*)\\"`)
match := aRE.FindSubmatch(msg)
assert.Equal(t, 2, len(match))
linkedPath := match[1]
err = getWatchPage(ctx, t, fmt.Sprintf("http://%s/%s", watchURL, linkedPath))
assert.Success(t, err)
successRE := regexp.MustCompile(`broadcasting update to 1 client`)
_, err = waitLogs(ctx, stderr, successRE)
assert.Success(t, err)
},
},
{
name: "watch-imported-file",
run: func(t *testing.T, ctx context.Context, dir string, env *xos.Env) {
writeFile(t, dir, "a.d2", `
...@b
`)
writeFile(t, dir, "b.d2", `
x
`)
stderr := &bytes.Buffer{}
tms := testMain(dir, env, "--watch", "--browser=0", "a.d2")
tms.Stderr = stderr
tms.Start(t, ctx)
defer func() {
err := tms.Signal(ctx, os.Interrupt)
assert.Success(t, err)
}()
// Wait for first compilation to finish
doneRE := regexp.MustCompile(`successfully compiled a.d2`)
_, err := waitLogs(ctx, stderr, doneRE)
assert.Success(t, err)
stderr.Reset()
// Test that writing an imported file will cause recompilation
writeFile(t, dir, "b.d2", `
x -> y
`)
bRE := regexp.MustCompile(`detected change in b.d2`)
_, err = waitLogs(ctx, stderr, bRE)
assert.Success(t, err)
stderr.Reset()
// Test burst of both files changing
writeFile(t, dir, "a.d2", `
...@b
hey
`)
writeFile(t, dir, "b.d2", `
x
hi
`)
bothRE := regexp.MustCompile(`detected change in a.d2, b.d2`)
_, err = waitLogs(ctx, stderr, bothRE)
assert.Success(t, err)
// Wait for that compilation to fully finish
_, err = waitLogs(ctx, stderr, doneRE)
assert.Success(t, err)
stderr.Reset()
// Update the main file to no longer have that dependency
writeFile(t, dir, "a.d2", `
a
`)
_, err = waitLogs(ctx, stderr, doneRE)
assert.Success(t, err)
stderr.Reset()
// Change b
writeFile(t, dir, "b.d2", `
y
`)
// Change a to retrigger compilation
// The test works by seeing that the report only says "a" changed, otherwise testing for omission of compilation from "b" would require waiting
writeFile(t, dir, "a.d2", `
c
`)
_, err = waitLogs(ctx, stderr, doneRE)
assert.Success(t, err)
},
},
}
ctx := context.Background()
@ -640,3 +875,48 @@ func testdataIgnoreDiff(tb testing.TB, ext string, got []byte) {
func getNumBoards(svg string) int {
return strings.Count(svg, `class="d2`)
}
var errRE = regexp.MustCompile(`err:`)
func waitLogs(ctx context.Context, buf *bytes.Buffer, pattern *regexp.Regexp) (string, error) {
ticker := time.NewTicker(10 * time.Millisecond)
defer ticker.Stop()
var match string
for i := 0; i < 100 && match == ""; i++ {
select {
case <-ticker.C:
out := buf.String()
match = pattern.FindString(out)
errMatch := errRE.FindString(out)
if errMatch != "" {
return "", errors.New(buf.String())
}
case <-ctx.Done():
ticker.Stop()
return "", fmt.Errorf("could not match pattern in log. logs: %s", buf.String())
}
}
if match == "" {
return "", errors.New(buf.String())
}
return match, nil
}
func getWatchPage(ctx context.Context, t *testing.T, page string) error {
req, err := http.NewRequestWithContext(ctx, "GET", page, nil)
if err != nil {
return err
}
var httpClient = &http.Client{}
resp, err := httpClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("status code: %d", resp.StatusCode)
}
return nil
}

View file

@ -2868,6 +2868,9 @@ y: profits {
loadFromFile(t, "grid_outside_labels"),
loadFromFile(t, "grid_edge_across_cell"),
loadFromFile(t, "nesting_power"),
loadFromFile(t, "unfilled_triangle"),
loadFromFile(t, "grid_container_dimensions"),
loadFromFile(t, "grid_label_positions"),
}
runa(t, tcs)

View file

@ -0,0 +1,11 @@
grid: {
width: 200
height: 200
grid-gap: 0
grid-rows: 2
grid-columns: 2
a
b
c
d
}

View file

@ -0,0 +1,154 @@
vars: {
grid: {
grid-rows: 2
grid-columns: 5
a
b
c
d
e
f
g
h
i
j
grid-gap: 0
}
}
direction: down
**.blank*: {
label: ""
style: {
stroke: transparent
fill: transparent
}
}
blank*: {
label: ""
style: {
stroke: transparent
fill: transparent
}
}
blank_ot -> blank_t -> blank_c -> blank_b -> blank_ob
blank_ot: {
blank1
OutsideTopLeft: {
label.near: outside-top-left
...${grid}
}
OutsideTopCenter: {
label.near: outside-top-center
...${grid}
}
OutsideTopRight: {
label.near: outside-top-right
...${grid}
}
blank2
}
blank_t: {
OutsideLeftTop: {
label.near: outside-left-top
...${grid}
}
TopLeft: {
label.near: top-left
...${grid}
}
TopCenter: {
label.near: top-center
...${grid}
}
TopRight: {
label.near: top-right
...${grid}
}
OutsideRightTop: {
label.near: outside-right-top
...${grid}
}
}
blank_c: {
OutsideLeftCenter: {
label.near: outside-left-center
...${grid}
}
CenterLeft: {
label.near: center-left
...${grid}
}
CenterCenter: {
label.near: center-center
...${grid}
}
CenterRight: {
label.near: center-right
...${grid}
}
OutsideRightCenter: {
label.near: outside-right-center
...${grid}
}
}
blank_b: {
OutsideLeftBottom: {
label.near: outside-left-bottom
...${grid}
}
BottomLeft: {
label.near: bottom-left
...${grid}
}
BottomCenter: {
label.near: bottom-center
...${grid}
}
BottomRight: {
label.near: bottom-right
...${grid}
}
OutsideRightBottom: {
label.near: outside-right-bottom
...${grid}
}
}
blank_ob: {
blank3
OutsideBottomLeft: {
label.near: outside-bottom-left
...${grid}
}
OutsideBottomCenter: {
label.near: outside-bottom-center
...${grid}
}
OutsideBottomRight: {
label.near: outside-bottom-right
...${grid}
}
blank4
}

View file

@ -0,0 +1,16 @@
direction: right
A <-> B: default {
source-arrowhead.style.filled: false
target-arrowhead.style.filled: false
}
C <-> D: triangle {
source-arrowhead: {
shape: triangle
style.filled: false
}
target-arrowhead: {
shape: triangle
style.filled: false
}
}

View file

@ -40,7 +40,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},

View file

@ -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.6.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 297 200"><svg id="d2-svg" class="d2-3501266100" width="297" height="200" viewBox="-1 -1 297 200"><rect x="-1.000000" y="-1.000000" width="297.000000" height="200.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
.d2-3501266100 .text-bold {
font-family: "d2-3501266100-font-bold";
<?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.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 297 200"><svg id="d2-svg" class="d2-3034358831" width="297" height="200" viewBox="-1 -1 297 200"><rect x="-1.000000" y="-1.000000" width="297.000000" height="200.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
.d2-3034358831 .text-bold {
font-family: "d2-3034358831-font-bold";
}
@font-face {
font-family: d2-3501266100-font-bold;
font-family: d2-3034358831-font-bold;
src: url("data:application/font-woff;base64,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");
}]]></style><style type="text/css"><![CDATA[.shape {
shape-rendering: geometricPrecision;
@ -18,78 +18,78 @@
opacity: 0.5;
}
.d2-3501266100 .fill-N1{fill:#0A0F25;}
.d2-3501266100 .fill-N2{fill:#676C7E;}
.d2-3501266100 .fill-N3{fill:#9499AB;}
.d2-3501266100 .fill-N4{fill:#CFD2DD;}
.d2-3501266100 .fill-N5{fill:#DEE1EB;}
.d2-3501266100 .fill-N6{fill:#EEF1F8;}
.d2-3501266100 .fill-N7{fill:#FFFFFF;}
.d2-3501266100 .fill-B1{fill:#0D32B2;}
.d2-3501266100 .fill-B2{fill:#0D32B2;}
.d2-3501266100 .fill-B3{fill:#E3E9FD;}
.d2-3501266100 .fill-B4{fill:#E3E9FD;}
.d2-3501266100 .fill-B5{fill:#EDF0FD;}
.d2-3501266100 .fill-B6{fill:#F7F8FE;}
.d2-3501266100 .fill-AA2{fill:#4A6FF3;}
.d2-3501266100 .fill-AA4{fill:#EDF0FD;}
.d2-3501266100 .fill-AA5{fill:#F7F8FE;}
.d2-3501266100 .fill-AB4{fill:#EDF0FD;}
.d2-3501266100 .fill-AB5{fill:#F7F8FE;}
.d2-3501266100 .stroke-N1{stroke:#0A0F25;}
.d2-3501266100 .stroke-N2{stroke:#676C7E;}
.d2-3501266100 .stroke-N3{stroke:#9499AB;}
.d2-3501266100 .stroke-N4{stroke:#CFD2DD;}
.d2-3501266100 .stroke-N5{stroke:#DEE1EB;}
.d2-3501266100 .stroke-N6{stroke:#EEF1F8;}
.d2-3501266100 .stroke-N7{stroke:#FFFFFF;}
.d2-3501266100 .stroke-B1{stroke:#0D32B2;}
.d2-3501266100 .stroke-B2{stroke:#0D32B2;}
.d2-3501266100 .stroke-B3{stroke:#E3E9FD;}
.d2-3501266100 .stroke-B4{stroke:#E3E9FD;}
.d2-3501266100 .stroke-B5{stroke:#EDF0FD;}
.d2-3501266100 .stroke-B6{stroke:#F7F8FE;}
.d2-3501266100 .stroke-AA2{stroke:#4A6FF3;}
.d2-3501266100 .stroke-AA4{stroke:#EDF0FD;}
.d2-3501266100 .stroke-AA5{stroke:#F7F8FE;}
.d2-3501266100 .stroke-AB4{stroke:#EDF0FD;}
.d2-3501266100 .stroke-AB5{stroke:#F7F8FE;}
.d2-3501266100 .background-color-N1{background-color:#0A0F25;}
.d2-3501266100 .background-color-N2{background-color:#676C7E;}
.d2-3501266100 .background-color-N3{background-color:#9499AB;}
.d2-3501266100 .background-color-N4{background-color:#CFD2DD;}
.d2-3501266100 .background-color-N5{background-color:#DEE1EB;}
.d2-3501266100 .background-color-N6{background-color:#EEF1F8;}
.d2-3501266100 .background-color-N7{background-color:#FFFFFF;}
.d2-3501266100 .background-color-B1{background-color:#0D32B2;}
.d2-3501266100 .background-color-B2{background-color:#0D32B2;}
.d2-3501266100 .background-color-B3{background-color:#E3E9FD;}
.d2-3501266100 .background-color-B4{background-color:#E3E9FD;}
.d2-3501266100 .background-color-B5{background-color:#EDF0FD;}
.d2-3501266100 .background-color-B6{background-color:#F7F8FE;}
.d2-3501266100 .background-color-AA2{background-color:#4A6FF3;}
.d2-3501266100 .background-color-AA4{background-color:#EDF0FD;}
.d2-3501266100 .background-color-AA5{background-color:#F7F8FE;}
.d2-3501266100 .background-color-AB4{background-color:#EDF0FD;}
.d2-3501266100 .background-color-AB5{background-color:#F7F8FE;}
.d2-3501266100 .color-N1{color:#0A0F25;}
.d2-3501266100 .color-N2{color:#676C7E;}
.d2-3501266100 .color-N3{color:#9499AB;}
.d2-3501266100 .color-N4{color:#CFD2DD;}
.d2-3501266100 .color-N5{color:#DEE1EB;}
.d2-3501266100 .color-N6{color:#EEF1F8;}
.d2-3501266100 .color-N7{color:#FFFFFF;}
.d2-3501266100 .color-B1{color:#0D32B2;}
.d2-3501266100 .color-B2{color:#0D32B2;}
.d2-3501266100 .color-B3{color:#E3E9FD;}
.d2-3501266100 .color-B4{color:#E3E9FD;}
.d2-3501266100 .color-B5{color:#EDF0FD;}
.d2-3501266100 .color-B6{color:#F7F8FE;}
.d2-3501266100 .color-AA2{color:#4A6FF3;}
.d2-3501266100 .color-AA4{color:#EDF0FD;}
.d2-3501266100 .color-AA5{color:#F7F8FE;}
.d2-3501266100 .color-AB4{color:#EDF0FD;}
.d2-3501266100 .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="grid"><g class="shape" ><rect x="0.000000" y="0.000000" width="295.000000" height="198.000000" class=" stroke-B1 fill-B4" style="stroke-width:2;" /></g></g><g id="grid.first"><g class="shape" ><rect x="100.000000" y="0.000000" width="95.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="147.500000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">first</text></g><g id="grid.second"><g class="shape" ><rect x="100.000000" y="66.000000" width="95.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="147.500000" y="104.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">second</text></g><g id="grid.third"><g class="shape" ><rect x="100.000000" y="132.000000" width="95.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="147.500000" y="170.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">third</text></g><mask id="d2-3501266100" maskUnits="userSpaceOnUse" x="-1" y="-1" width="297" height="200">
.d2-3034358831 .fill-N1{fill:#0A0F25;}
.d2-3034358831 .fill-N2{fill:#676C7E;}
.d2-3034358831 .fill-N3{fill:#9499AB;}
.d2-3034358831 .fill-N4{fill:#CFD2DD;}
.d2-3034358831 .fill-N5{fill:#DEE1EB;}
.d2-3034358831 .fill-N6{fill:#EEF1F8;}
.d2-3034358831 .fill-N7{fill:#FFFFFF;}
.d2-3034358831 .fill-B1{fill:#0D32B2;}
.d2-3034358831 .fill-B2{fill:#0D32B2;}
.d2-3034358831 .fill-B3{fill:#E3E9FD;}
.d2-3034358831 .fill-B4{fill:#E3E9FD;}
.d2-3034358831 .fill-B5{fill:#EDF0FD;}
.d2-3034358831 .fill-B6{fill:#F7F8FE;}
.d2-3034358831 .fill-AA2{fill:#4A6FF3;}
.d2-3034358831 .fill-AA4{fill:#EDF0FD;}
.d2-3034358831 .fill-AA5{fill:#F7F8FE;}
.d2-3034358831 .fill-AB4{fill:#EDF0FD;}
.d2-3034358831 .fill-AB5{fill:#F7F8FE;}
.d2-3034358831 .stroke-N1{stroke:#0A0F25;}
.d2-3034358831 .stroke-N2{stroke:#676C7E;}
.d2-3034358831 .stroke-N3{stroke:#9499AB;}
.d2-3034358831 .stroke-N4{stroke:#CFD2DD;}
.d2-3034358831 .stroke-N5{stroke:#DEE1EB;}
.d2-3034358831 .stroke-N6{stroke:#EEF1F8;}
.d2-3034358831 .stroke-N7{stroke:#FFFFFF;}
.d2-3034358831 .stroke-B1{stroke:#0D32B2;}
.d2-3034358831 .stroke-B2{stroke:#0D32B2;}
.d2-3034358831 .stroke-B3{stroke:#E3E9FD;}
.d2-3034358831 .stroke-B4{stroke:#E3E9FD;}
.d2-3034358831 .stroke-B5{stroke:#EDF0FD;}
.d2-3034358831 .stroke-B6{stroke:#F7F8FE;}
.d2-3034358831 .stroke-AA2{stroke:#4A6FF3;}
.d2-3034358831 .stroke-AA4{stroke:#EDF0FD;}
.d2-3034358831 .stroke-AA5{stroke:#F7F8FE;}
.d2-3034358831 .stroke-AB4{stroke:#EDF0FD;}
.d2-3034358831 .stroke-AB5{stroke:#F7F8FE;}
.d2-3034358831 .background-color-N1{background-color:#0A0F25;}
.d2-3034358831 .background-color-N2{background-color:#676C7E;}
.d2-3034358831 .background-color-N3{background-color:#9499AB;}
.d2-3034358831 .background-color-N4{background-color:#CFD2DD;}
.d2-3034358831 .background-color-N5{background-color:#DEE1EB;}
.d2-3034358831 .background-color-N6{background-color:#EEF1F8;}
.d2-3034358831 .background-color-N7{background-color:#FFFFFF;}
.d2-3034358831 .background-color-B1{background-color:#0D32B2;}
.d2-3034358831 .background-color-B2{background-color:#0D32B2;}
.d2-3034358831 .background-color-B3{background-color:#E3E9FD;}
.d2-3034358831 .background-color-B4{background-color:#E3E9FD;}
.d2-3034358831 .background-color-B5{background-color:#EDF0FD;}
.d2-3034358831 .background-color-B6{background-color:#F7F8FE;}
.d2-3034358831 .background-color-AA2{background-color:#4A6FF3;}
.d2-3034358831 .background-color-AA4{background-color:#EDF0FD;}
.d2-3034358831 .background-color-AA5{background-color:#F7F8FE;}
.d2-3034358831 .background-color-AB4{background-color:#EDF0FD;}
.d2-3034358831 .background-color-AB5{background-color:#F7F8FE;}
.d2-3034358831 .color-N1{color:#0A0F25;}
.d2-3034358831 .color-N2{color:#676C7E;}
.d2-3034358831 .color-N3{color:#9499AB;}
.d2-3034358831 .color-N4{color:#CFD2DD;}
.d2-3034358831 .color-N5{color:#DEE1EB;}
.d2-3034358831 .color-N6{color:#EEF1F8;}
.d2-3034358831 .color-N7{color:#FFFFFF;}
.d2-3034358831 .color-B1{color:#0D32B2;}
.d2-3034358831 .color-B2{color:#0D32B2;}
.d2-3034358831 .color-B3{color:#E3E9FD;}
.d2-3034358831 .color-B4{color:#E3E9FD;}
.d2-3034358831 .color-B5{color:#EDF0FD;}
.d2-3034358831 .color-B6{color:#F7F8FE;}
.d2-3034358831 .color-AA2{color:#4A6FF3;}
.d2-3034358831 .color-AA4{color:#EDF0FD;}
.d2-3034358831 .color-AA5{color:#F7F8FE;}
.d2-3034358831 .color-AB4{color:#EDF0FD;}
.d2-3034358831 .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="grid"><g class="shape" ><rect x="0.000000" y="0.000000" width="295.000000" height="198.000000" class=" stroke-B1 fill-B4" style="stroke-width:2;" /></g></g><g id="grid.first"><g class="shape" ><rect x="100.000000" y="0.000000" width="95.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="147.500000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">first</text></g><g id="grid.second"><g class="shape" ><rect x="100.000000" y="66.000000" width="95.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="147.500000" y="104.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">second</text></g><g id="grid.third"><g class="shape" ><rect x="100.000000" y="132.000000" width="95.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="147.500000" y="170.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">third</text></g><mask id="d2-3034358831" maskUnits="userSpaceOnUse" x="-1" y="-1" width="297" height="200">
<rect x="-1" y="-1" width="297" height="200" fill="white"></rect>
<rect x="132.500000" y="22.500000" width="30" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="122.500000" y="88.500000" width="50" height="21" fill="rgba(0,0,0,0.75)"></rect>

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -40,7 +40,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},

View file

@ -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.6.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 297 200"><svg id="d2-svg" class="d2-3322634070" width="297" height="200" viewBox="11 11 297 200"><rect x="11.000000" y="11.000000" width="297.000000" height="200.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
.d2-3322634070 .text-bold {
font-family: "d2-3322634070-font-bold";
<?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.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 297 200"><svg id="d2-svg" class="d2-3033250019" width="297" height="200" viewBox="11 11 297 200"><rect x="11.000000" y="11.000000" width="297.000000" height="200.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
.d2-3033250019 .text-bold {
font-family: "d2-3033250019-font-bold";
}
@font-face {
font-family: d2-3322634070-font-bold;
font-family: d2-3033250019-font-bold;
src: url("data:application/font-woff;base64,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");
}]]></style><style type="text/css"><![CDATA[.shape {
shape-rendering: geometricPrecision;
@ -18,78 +18,78 @@
opacity: 0.5;
}
.d2-3322634070 .fill-N1{fill:#0A0F25;}
.d2-3322634070 .fill-N2{fill:#676C7E;}
.d2-3322634070 .fill-N3{fill:#9499AB;}
.d2-3322634070 .fill-N4{fill:#CFD2DD;}
.d2-3322634070 .fill-N5{fill:#DEE1EB;}
.d2-3322634070 .fill-N6{fill:#EEF1F8;}
.d2-3322634070 .fill-N7{fill:#FFFFFF;}
.d2-3322634070 .fill-B1{fill:#0D32B2;}
.d2-3322634070 .fill-B2{fill:#0D32B2;}
.d2-3322634070 .fill-B3{fill:#E3E9FD;}
.d2-3322634070 .fill-B4{fill:#E3E9FD;}
.d2-3322634070 .fill-B5{fill:#EDF0FD;}
.d2-3322634070 .fill-B6{fill:#F7F8FE;}
.d2-3322634070 .fill-AA2{fill:#4A6FF3;}
.d2-3322634070 .fill-AA4{fill:#EDF0FD;}
.d2-3322634070 .fill-AA5{fill:#F7F8FE;}
.d2-3322634070 .fill-AB4{fill:#EDF0FD;}
.d2-3322634070 .fill-AB5{fill:#F7F8FE;}
.d2-3322634070 .stroke-N1{stroke:#0A0F25;}
.d2-3322634070 .stroke-N2{stroke:#676C7E;}
.d2-3322634070 .stroke-N3{stroke:#9499AB;}
.d2-3322634070 .stroke-N4{stroke:#CFD2DD;}
.d2-3322634070 .stroke-N5{stroke:#DEE1EB;}
.d2-3322634070 .stroke-N6{stroke:#EEF1F8;}
.d2-3322634070 .stroke-N7{stroke:#FFFFFF;}
.d2-3322634070 .stroke-B1{stroke:#0D32B2;}
.d2-3322634070 .stroke-B2{stroke:#0D32B2;}
.d2-3322634070 .stroke-B3{stroke:#E3E9FD;}
.d2-3322634070 .stroke-B4{stroke:#E3E9FD;}
.d2-3322634070 .stroke-B5{stroke:#EDF0FD;}
.d2-3322634070 .stroke-B6{stroke:#F7F8FE;}
.d2-3322634070 .stroke-AA2{stroke:#4A6FF3;}
.d2-3322634070 .stroke-AA4{stroke:#EDF0FD;}
.d2-3322634070 .stroke-AA5{stroke:#F7F8FE;}
.d2-3322634070 .stroke-AB4{stroke:#EDF0FD;}
.d2-3322634070 .stroke-AB5{stroke:#F7F8FE;}
.d2-3322634070 .background-color-N1{background-color:#0A0F25;}
.d2-3322634070 .background-color-N2{background-color:#676C7E;}
.d2-3322634070 .background-color-N3{background-color:#9499AB;}
.d2-3322634070 .background-color-N4{background-color:#CFD2DD;}
.d2-3322634070 .background-color-N5{background-color:#DEE1EB;}
.d2-3322634070 .background-color-N6{background-color:#EEF1F8;}
.d2-3322634070 .background-color-N7{background-color:#FFFFFF;}
.d2-3322634070 .background-color-B1{background-color:#0D32B2;}
.d2-3322634070 .background-color-B2{background-color:#0D32B2;}
.d2-3322634070 .background-color-B3{background-color:#E3E9FD;}
.d2-3322634070 .background-color-B4{background-color:#E3E9FD;}
.d2-3322634070 .background-color-B5{background-color:#EDF0FD;}
.d2-3322634070 .background-color-B6{background-color:#F7F8FE;}
.d2-3322634070 .background-color-AA2{background-color:#4A6FF3;}
.d2-3322634070 .background-color-AA4{background-color:#EDF0FD;}
.d2-3322634070 .background-color-AA5{background-color:#F7F8FE;}
.d2-3322634070 .background-color-AB4{background-color:#EDF0FD;}
.d2-3322634070 .background-color-AB5{background-color:#F7F8FE;}
.d2-3322634070 .color-N1{color:#0A0F25;}
.d2-3322634070 .color-N2{color:#676C7E;}
.d2-3322634070 .color-N3{color:#9499AB;}
.d2-3322634070 .color-N4{color:#CFD2DD;}
.d2-3322634070 .color-N5{color:#DEE1EB;}
.d2-3322634070 .color-N6{color:#EEF1F8;}
.d2-3322634070 .color-N7{color:#FFFFFF;}
.d2-3322634070 .color-B1{color:#0D32B2;}
.d2-3322634070 .color-B2{color:#0D32B2;}
.d2-3322634070 .color-B3{color:#E3E9FD;}
.d2-3322634070 .color-B4{color:#E3E9FD;}
.d2-3322634070 .color-B5{color:#EDF0FD;}
.d2-3322634070 .color-B6{color:#F7F8FE;}
.d2-3322634070 .color-AA2{color:#4A6FF3;}
.d2-3322634070 .color-AA4{color:#EDF0FD;}
.d2-3322634070 .color-AA5{color:#F7F8FE;}
.d2-3322634070 .color-AB4{color:#EDF0FD;}
.d2-3322634070 .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="grid"><g class="shape" ><rect x="12.000000" y="12.000000" width="295.000000" height="198.000000" class=" stroke-B1 fill-B4" style="stroke-width:2;" /></g></g><g id="grid.first"><g class="shape" ><rect x="112.000000" y="12.000000" width="95.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="159.500000" y="50.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">first</text></g><g id="grid.second"><g class="shape" ><rect x="112.000000" y="78.000000" width="95.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="159.500000" y="116.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">second</text></g><g id="grid.third"><g class="shape" ><rect x="112.000000" y="144.000000" width="95.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="159.500000" y="182.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">third</text></g><mask id="d2-3322634070" maskUnits="userSpaceOnUse" x="11" y="11" width="297" height="200">
.d2-3033250019 .fill-N1{fill:#0A0F25;}
.d2-3033250019 .fill-N2{fill:#676C7E;}
.d2-3033250019 .fill-N3{fill:#9499AB;}
.d2-3033250019 .fill-N4{fill:#CFD2DD;}
.d2-3033250019 .fill-N5{fill:#DEE1EB;}
.d2-3033250019 .fill-N6{fill:#EEF1F8;}
.d2-3033250019 .fill-N7{fill:#FFFFFF;}
.d2-3033250019 .fill-B1{fill:#0D32B2;}
.d2-3033250019 .fill-B2{fill:#0D32B2;}
.d2-3033250019 .fill-B3{fill:#E3E9FD;}
.d2-3033250019 .fill-B4{fill:#E3E9FD;}
.d2-3033250019 .fill-B5{fill:#EDF0FD;}
.d2-3033250019 .fill-B6{fill:#F7F8FE;}
.d2-3033250019 .fill-AA2{fill:#4A6FF3;}
.d2-3033250019 .fill-AA4{fill:#EDF0FD;}
.d2-3033250019 .fill-AA5{fill:#F7F8FE;}
.d2-3033250019 .fill-AB4{fill:#EDF0FD;}
.d2-3033250019 .fill-AB5{fill:#F7F8FE;}
.d2-3033250019 .stroke-N1{stroke:#0A0F25;}
.d2-3033250019 .stroke-N2{stroke:#676C7E;}
.d2-3033250019 .stroke-N3{stroke:#9499AB;}
.d2-3033250019 .stroke-N4{stroke:#CFD2DD;}
.d2-3033250019 .stroke-N5{stroke:#DEE1EB;}
.d2-3033250019 .stroke-N6{stroke:#EEF1F8;}
.d2-3033250019 .stroke-N7{stroke:#FFFFFF;}
.d2-3033250019 .stroke-B1{stroke:#0D32B2;}
.d2-3033250019 .stroke-B2{stroke:#0D32B2;}
.d2-3033250019 .stroke-B3{stroke:#E3E9FD;}
.d2-3033250019 .stroke-B4{stroke:#E3E9FD;}
.d2-3033250019 .stroke-B5{stroke:#EDF0FD;}
.d2-3033250019 .stroke-B6{stroke:#F7F8FE;}
.d2-3033250019 .stroke-AA2{stroke:#4A6FF3;}
.d2-3033250019 .stroke-AA4{stroke:#EDF0FD;}
.d2-3033250019 .stroke-AA5{stroke:#F7F8FE;}
.d2-3033250019 .stroke-AB4{stroke:#EDF0FD;}
.d2-3033250019 .stroke-AB5{stroke:#F7F8FE;}
.d2-3033250019 .background-color-N1{background-color:#0A0F25;}
.d2-3033250019 .background-color-N2{background-color:#676C7E;}
.d2-3033250019 .background-color-N3{background-color:#9499AB;}
.d2-3033250019 .background-color-N4{background-color:#CFD2DD;}
.d2-3033250019 .background-color-N5{background-color:#DEE1EB;}
.d2-3033250019 .background-color-N6{background-color:#EEF1F8;}
.d2-3033250019 .background-color-N7{background-color:#FFFFFF;}
.d2-3033250019 .background-color-B1{background-color:#0D32B2;}
.d2-3033250019 .background-color-B2{background-color:#0D32B2;}
.d2-3033250019 .background-color-B3{background-color:#E3E9FD;}
.d2-3033250019 .background-color-B4{background-color:#E3E9FD;}
.d2-3033250019 .background-color-B5{background-color:#EDF0FD;}
.d2-3033250019 .background-color-B6{background-color:#F7F8FE;}
.d2-3033250019 .background-color-AA2{background-color:#4A6FF3;}
.d2-3033250019 .background-color-AA4{background-color:#EDF0FD;}
.d2-3033250019 .background-color-AA5{background-color:#F7F8FE;}
.d2-3033250019 .background-color-AB4{background-color:#EDF0FD;}
.d2-3033250019 .background-color-AB5{background-color:#F7F8FE;}
.d2-3033250019 .color-N1{color:#0A0F25;}
.d2-3033250019 .color-N2{color:#676C7E;}
.d2-3033250019 .color-N3{color:#9499AB;}
.d2-3033250019 .color-N4{color:#CFD2DD;}
.d2-3033250019 .color-N5{color:#DEE1EB;}
.d2-3033250019 .color-N6{color:#EEF1F8;}
.d2-3033250019 .color-N7{color:#FFFFFF;}
.d2-3033250019 .color-B1{color:#0D32B2;}
.d2-3033250019 .color-B2{color:#0D32B2;}
.d2-3033250019 .color-B3{color:#E3E9FD;}
.d2-3033250019 .color-B4{color:#E3E9FD;}
.d2-3033250019 .color-B5{color:#EDF0FD;}
.d2-3033250019 .color-B6{color:#F7F8FE;}
.d2-3033250019 .color-AA2{color:#4A6FF3;}
.d2-3033250019 .color-AA4{color:#EDF0FD;}
.d2-3033250019 .color-AA5{color:#F7F8FE;}
.d2-3033250019 .color-AB4{color:#EDF0FD;}
.d2-3033250019 .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="grid"><g class="shape" ><rect x="12.000000" y="12.000000" width="295.000000" height="198.000000" class=" stroke-B1 fill-B4" style="stroke-width:2;" /></g></g><g id="grid.first"><g class="shape" ><rect x="112.000000" y="12.000000" width="95.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="159.500000" y="50.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">first</text></g><g id="grid.second"><g class="shape" ><rect x="112.000000" y="78.000000" width="95.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="159.500000" y="116.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">second</text></g><g id="grid.third"><g class="shape" ><rect x="112.000000" y="144.000000" width="95.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="159.500000" y="182.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">third</text></g><mask id="d2-3033250019" maskUnits="userSpaceOnUse" x="11" y="11" width="297" height="200">
<rect x="11" y="11" width="297" height="200" fill="white"></rect>
<rect x="144.500000" y="34.500000" width="30" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="134.500000" y="100.500000" width="50" height="21" fill="rgba(0,0,0,0.75)"></rect>

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View file

@ -0,0 +1,253 @@
{
"name": "",
"isFolderOnly": false,
"fontFamily": "SourceSansPro",
"shapes": [
{
"id": "grid",
"type": "rectangle",
"pos": {
"x": 0,
"y": 0
},
"width": 200,
"height": 200,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B4",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "grid",
"fontSize": 28,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 44,
"labelHeight": 36,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "grid.a",
"type": "rectangle",
"pos": {
"x": 46,
"y": 57
},
"width": 53,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "a",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 8,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "grid.b",
"type": "rectangle",
"pos": {
"x": 99,
"y": 57
},
"width": 54,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "b",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 8,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "grid.c",
"type": "rectangle",
"pos": {
"x": 46,
"y": 123
},
"width": 53,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "c",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 8,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "grid.d",
"type": "rectangle",
"pos": {
"x": 99,
"y": 123
},
"width": 54,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "d",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 9,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
}
],
"connections": [],
"root": {
"id": "",
"type": "",
"pos": {
"x": 0,
"y": 0
},
"width": 0,
"height": 0,
"opacity": 0,
"strokeDash": 0,
"strokeWidth": 0,
"borderRadius": 0,
"fill": "N7",
"stroke": "",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "",
"fontSize": 0,
"fontFamily": "",
"language": "",
"color": "",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"zIndex": 0,
"level": 0
}
}

View file

@ -0,0 +1,106 @@
<?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.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 202 202"><svg id="d2-svg" class="d2-3242100033" width="202" height="202" viewBox="-1 -1 202 202"><rect x="-1.000000" y="-1.000000" width="202.000000" height="202.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
.d2-3242100033 .text {
font-family: "d2-3242100033-font-regular";
}
@font-face {
font-family: d2-3242100033-font-regular;
src: url("data:application/font-woff;base64,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");
}
.d2-3242100033 .text-bold {
font-family: "d2-3242100033-font-bold";
}
@font-face {
font-family: d2-3242100033-font-bold;
src: url("data:application/font-woff;base64,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");
}]]></style><style type="text/css"><![CDATA[.shape {
shape-rendering: geometricPrecision;
stroke-linejoin: round;
}
.connection {
stroke-linecap: round;
stroke-linejoin: round;
}
.blend {
mix-blend-mode: multiply;
opacity: 0.5;
}
.d2-3242100033 .fill-N1{fill:#0A0F25;}
.d2-3242100033 .fill-N2{fill:#676C7E;}
.d2-3242100033 .fill-N3{fill:#9499AB;}
.d2-3242100033 .fill-N4{fill:#CFD2DD;}
.d2-3242100033 .fill-N5{fill:#DEE1EB;}
.d2-3242100033 .fill-N6{fill:#EEF1F8;}
.d2-3242100033 .fill-N7{fill:#FFFFFF;}
.d2-3242100033 .fill-B1{fill:#0D32B2;}
.d2-3242100033 .fill-B2{fill:#0D32B2;}
.d2-3242100033 .fill-B3{fill:#E3E9FD;}
.d2-3242100033 .fill-B4{fill:#E3E9FD;}
.d2-3242100033 .fill-B5{fill:#EDF0FD;}
.d2-3242100033 .fill-B6{fill:#F7F8FE;}
.d2-3242100033 .fill-AA2{fill:#4A6FF3;}
.d2-3242100033 .fill-AA4{fill:#EDF0FD;}
.d2-3242100033 .fill-AA5{fill:#F7F8FE;}
.d2-3242100033 .fill-AB4{fill:#EDF0FD;}
.d2-3242100033 .fill-AB5{fill:#F7F8FE;}
.d2-3242100033 .stroke-N1{stroke:#0A0F25;}
.d2-3242100033 .stroke-N2{stroke:#676C7E;}
.d2-3242100033 .stroke-N3{stroke:#9499AB;}
.d2-3242100033 .stroke-N4{stroke:#CFD2DD;}
.d2-3242100033 .stroke-N5{stroke:#DEE1EB;}
.d2-3242100033 .stroke-N6{stroke:#EEF1F8;}
.d2-3242100033 .stroke-N7{stroke:#FFFFFF;}
.d2-3242100033 .stroke-B1{stroke:#0D32B2;}
.d2-3242100033 .stroke-B2{stroke:#0D32B2;}
.d2-3242100033 .stroke-B3{stroke:#E3E9FD;}
.d2-3242100033 .stroke-B4{stroke:#E3E9FD;}
.d2-3242100033 .stroke-B5{stroke:#EDF0FD;}
.d2-3242100033 .stroke-B6{stroke:#F7F8FE;}
.d2-3242100033 .stroke-AA2{stroke:#4A6FF3;}
.d2-3242100033 .stroke-AA4{stroke:#EDF0FD;}
.d2-3242100033 .stroke-AA5{stroke:#F7F8FE;}
.d2-3242100033 .stroke-AB4{stroke:#EDF0FD;}
.d2-3242100033 .stroke-AB5{stroke:#F7F8FE;}
.d2-3242100033 .background-color-N1{background-color:#0A0F25;}
.d2-3242100033 .background-color-N2{background-color:#676C7E;}
.d2-3242100033 .background-color-N3{background-color:#9499AB;}
.d2-3242100033 .background-color-N4{background-color:#CFD2DD;}
.d2-3242100033 .background-color-N5{background-color:#DEE1EB;}
.d2-3242100033 .background-color-N6{background-color:#EEF1F8;}
.d2-3242100033 .background-color-N7{background-color:#FFFFFF;}
.d2-3242100033 .background-color-B1{background-color:#0D32B2;}
.d2-3242100033 .background-color-B2{background-color:#0D32B2;}
.d2-3242100033 .background-color-B3{background-color:#E3E9FD;}
.d2-3242100033 .background-color-B4{background-color:#E3E9FD;}
.d2-3242100033 .background-color-B5{background-color:#EDF0FD;}
.d2-3242100033 .background-color-B6{background-color:#F7F8FE;}
.d2-3242100033 .background-color-AA2{background-color:#4A6FF3;}
.d2-3242100033 .background-color-AA4{background-color:#EDF0FD;}
.d2-3242100033 .background-color-AA5{background-color:#F7F8FE;}
.d2-3242100033 .background-color-AB4{background-color:#EDF0FD;}
.d2-3242100033 .background-color-AB5{background-color:#F7F8FE;}
.d2-3242100033 .color-N1{color:#0A0F25;}
.d2-3242100033 .color-N2{color:#676C7E;}
.d2-3242100033 .color-N3{color:#9499AB;}
.d2-3242100033 .color-N4{color:#CFD2DD;}
.d2-3242100033 .color-N5{color:#DEE1EB;}
.d2-3242100033 .color-N6{color:#EEF1F8;}
.d2-3242100033 .color-N7{color:#FFFFFF;}
.d2-3242100033 .color-B1{color:#0D32B2;}
.d2-3242100033 .color-B2{color:#0D32B2;}
.d2-3242100033 .color-B3{color:#E3E9FD;}
.d2-3242100033 .color-B4{color:#E3E9FD;}
.d2-3242100033 .color-B5{color:#EDF0FD;}
.d2-3242100033 .color-B6{color:#F7F8FE;}
.d2-3242100033 .color-AA2{color:#4A6FF3;}
.d2-3242100033 .color-AA4{color:#EDF0FD;}
.d2-3242100033 .color-AA5{color:#F7F8FE;}
.d2-3242100033 .color-AB4{color:#EDF0FD;}
.d2-3242100033 .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="grid"><g class="shape" ><rect x="0.000000" y="0.000000" width="200.000000" height="200.000000" class=" stroke-B1 fill-B4" style="stroke-width:2;" /></g><text x="100.000000" y="33.000000" class="text fill-N1" style="text-anchor:middle;font-size:28px">grid</text></g><g id="grid.a"><g class="shape" ><rect x="46.000000" y="57.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="72.500000" y="95.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">a</text></g><g id="grid.b"><g class="shape" ><rect x="99.000000" y="57.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="126.000000" y="95.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">b</text></g><g id="grid.c"><g class="shape" ><rect x="46.000000" y="123.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="72.500000" y="161.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">c</text></g><g id="grid.d"><g class="shape" ><rect x="99.000000" y="123.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="126.000000" y="161.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">d</text></g><mask id="d2-3242100033" maskUnits="userSpaceOnUse" x="-1" y="-1" width="202" height="202">
<rect x="-1" y="-1" width="202" height="202" fill="white"></rect>
<rect x="78.000000" y="5.000000" width="44" height="36" fill="rgba(0,0,0,0.75)"></rect>
<rect x="68.500000" y="79.500000" width="8" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="122.000000" y="79.500000" width="8" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="68.500000" y="145.500000" width="8" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="121.500000" y="145.500000" width="9" height="21" fill="rgba(0,0,0,0.75)"></rect>
</mask></svg></svg>

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -0,0 +1,253 @@
{
"name": "",
"isFolderOnly": false,
"fontFamily": "SourceSansPro",
"shapes": [
{
"id": "grid",
"type": "rectangle",
"pos": {
"x": 12,
"y": 12
},
"width": 200,
"height": 200,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B4",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "grid",
"fontSize": 28,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 44,
"labelHeight": 36,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "grid.a",
"type": "rectangle",
"pos": {
"x": 58,
"y": 69
},
"width": 53,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "a",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 8,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "grid.b",
"type": "rectangle",
"pos": {
"x": 111,
"y": 69
},
"width": 54,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "b",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 8,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "grid.c",
"type": "rectangle",
"pos": {
"x": 58,
"y": 135
},
"width": 53,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "c",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 8,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "grid.d",
"type": "rectangle",
"pos": {
"x": 111,
"y": 135
},
"width": 54,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "d",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 9,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 2
}
],
"connections": [],
"root": {
"id": "",
"type": "",
"pos": {
"x": 0,
"y": 0
},
"width": 0,
"height": 0,
"opacity": 0,
"strokeDash": 0,
"strokeWidth": 0,
"borderRadius": 0,
"fill": "N7",
"stroke": "",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "",
"fontSize": 0,
"fontFamily": "",
"language": "",
"color": "",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"zIndex": 0,
"level": 0
}
}

View file

@ -0,0 +1,106 @@
<?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.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 202 202"><svg id="d2-svg" class="d2-2921019823" width="202" height="202" viewBox="11 11 202 202"><rect x="11.000000" y="11.000000" width="202.000000" height="202.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
.d2-2921019823 .text {
font-family: "d2-2921019823-font-regular";
}
@font-face {
font-family: d2-2921019823-font-regular;
src: url("data:application/font-woff;base64,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");
}
.d2-2921019823 .text-bold {
font-family: "d2-2921019823-font-bold";
}
@font-face {
font-family: d2-2921019823-font-bold;
src: url("data:application/font-woff;base64,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");
}]]></style><style type="text/css"><![CDATA[.shape {
shape-rendering: geometricPrecision;
stroke-linejoin: round;
}
.connection {
stroke-linecap: round;
stroke-linejoin: round;
}
.blend {
mix-blend-mode: multiply;
opacity: 0.5;
}
.d2-2921019823 .fill-N1{fill:#0A0F25;}
.d2-2921019823 .fill-N2{fill:#676C7E;}
.d2-2921019823 .fill-N3{fill:#9499AB;}
.d2-2921019823 .fill-N4{fill:#CFD2DD;}
.d2-2921019823 .fill-N5{fill:#DEE1EB;}
.d2-2921019823 .fill-N6{fill:#EEF1F8;}
.d2-2921019823 .fill-N7{fill:#FFFFFF;}
.d2-2921019823 .fill-B1{fill:#0D32B2;}
.d2-2921019823 .fill-B2{fill:#0D32B2;}
.d2-2921019823 .fill-B3{fill:#E3E9FD;}
.d2-2921019823 .fill-B4{fill:#E3E9FD;}
.d2-2921019823 .fill-B5{fill:#EDF0FD;}
.d2-2921019823 .fill-B6{fill:#F7F8FE;}
.d2-2921019823 .fill-AA2{fill:#4A6FF3;}
.d2-2921019823 .fill-AA4{fill:#EDF0FD;}
.d2-2921019823 .fill-AA5{fill:#F7F8FE;}
.d2-2921019823 .fill-AB4{fill:#EDF0FD;}
.d2-2921019823 .fill-AB5{fill:#F7F8FE;}
.d2-2921019823 .stroke-N1{stroke:#0A0F25;}
.d2-2921019823 .stroke-N2{stroke:#676C7E;}
.d2-2921019823 .stroke-N3{stroke:#9499AB;}
.d2-2921019823 .stroke-N4{stroke:#CFD2DD;}
.d2-2921019823 .stroke-N5{stroke:#DEE1EB;}
.d2-2921019823 .stroke-N6{stroke:#EEF1F8;}
.d2-2921019823 .stroke-N7{stroke:#FFFFFF;}
.d2-2921019823 .stroke-B1{stroke:#0D32B2;}
.d2-2921019823 .stroke-B2{stroke:#0D32B2;}
.d2-2921019823 .stroke-B3{stroke:#E3E9FD;}
.d2-2921019823 .stroke-B4{stroke:#E3E9FD;}
.d2-2921019823 .stroke-B5{stroke:#EDF0FD;}
.d2-2921019823 .stroke-B6{stroke:#F7F8FE;}
.d2-2921019823 .stroke-AA2{stroke:#4A6FF3;}
.d2-2921019823 .stroke-AA4{stroke:#EDF0FD;}
.d2-2921019823 .stroke-AA5{stroke:#F7F8FE;}
.d2-2921019823 .stroke-AB4{stroke:#EDF0FD;}
.d2-2921019823 .stroke-AB5{stroke:#F7F8FE;}
.d2-2921019823 .background-color-N1{background-color:#0A0F25;}
.d2-2921019823 .background-color-N2{background-color:#676C7E;}
.d2-2921019823 .background-color-N3{background-color:#9499AB;}
.d2-2921019823 .background-color-N4{background-color:#CFD2DD;}
.d2-2921019823 .background-color-N5{background-color:#DEE1EB;}
.d2-2921019823 .background-color-N6{background-color:#EEF1F8;}
.d2-2921019823 .background-color-N7{background-color:#FFFFFF;}
.d2-2921019823 .background-color-B1{background-color:#0D32B2;}
.d2-2921019823 .background-color-B2{background-color:#0D32B2;}
.d2-2921019823 .background-color-B3{background-color:#E3E9FD;}
.d2-2921019823 .background-color-B4{background-color:#E3E9FD;}
.d2-2921019823 .background-color-B5{background-color:#EDF0FD;}
.d2-2921019823 .background-color-B6{background-color:#F7F8FE;}
.d2-2921019823 .background-color-AA2{background-color:#4A6FF3;}
.d2-2921019823 .background-color-AA4{background-color:#EDF0FD;}
.d2-2921019823 .background-color-AA5{background-color:#F7F8FE;}
.d2-2921019823 .background-color-AB4{background-color:#EDF0FD;}
.d2-2921019823 .background-color-AB5{background-color:#F7F8FE;}
.d2-2921019823 .color-N1{color:#0A0F25;}
.d2-2921019823 .color-N2{color:#676C7E;}
.d2-2921019823 .color-N3{color:#9499AB;}
.d2-2921019823 .color-N4{color:#CFD2DD;}
.d2-2921019823 .color-N5{color:#DEE1EB;}
.d2-2921019823 .color-N6{color:#EEF1F8;}
.d2-2921019823 .color-N7{color:#FFFFFF;}
.d2-2921019823 .color-B1{color:#0D32B2;}
.d2-2921019823 .color-B2{color:#0D32B2;}
.d2-2921019823 .color-B3{color:#E3E9FD;}
.d2-2921019823 .color-B4{color:#E3E9FD;}
.d2-2921019823 .color-B5{color:#EDF0FD;}
.d2-2921019823 .color-B6{color:#F7F8FE;}
.d2-2921019823 .color-AA2{color:#4A6FF3;}
.d2-2921019823 .color-AA4{color:#EDF0FD;}
.d2-2921019823 .color-AA5{color:#F7F8FE;}
.d2-2921019823 .color-AB4{color:#EDF0FD;}
.d2-2921019823 .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="grid"><g class="shape" ><rect x="12.000000" y="12.000000" width="200.000000" height="200.000000" class=" stroke-B1 fill-B4" style="stroke-width:2;" /></g><text x="112.000000" y="45.000000" class="text fill-N1" style="text-anchor:middle;font-size:28px">grid</text></g><g id="grid.a"><g class="shape" ><rect x="58.000000" y="69.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="84.500000" y="107.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">a</text></g><g id="grid.b"><g class="shape" ><rect x="111.000000" y="69.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="138.000000" y="107.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">b</text></g><g id="grid.c"><g class="shape" ><rect x="58.000000" y="135.000000" width="53.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="84.500000" y="173.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">c</text></g><g id="grid.d"><g class="shape" ><rect x="111.000000" y="135.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="138.000000" y="173.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">d</text></g><mask id="d2-2921019823" maskUnits="userSpaceOnUse" x="11" y="11" width="202" height="202">
<rect x="11" y="11" width="202" height="202" fill="white"></rect>
<rect x="90.000000" y="17.000000" width="44" height="36" fill="rgba(0,0,0,0.75)"></rect>
<rect x="80.500000" y="91.500000" width="8" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="134.000000" y="91.500000" width="8" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="80.500000" y="157.500000" width="8" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="133.500000" y="157.500000" width="9" height="21" fill="rgba(0,0,0,0.75)"></rect>
</mask></svg></svg>

After

Width:  |  Height:  |  Size: 14 KiB

View file

@ -274,7 +274,7 @@
"x": 424,
"y": 58
},
"width": 278,
"width": 295,
"height": 306,
"opacity": 1,
"strokeDash": 0,
@ -324,7 +324,7 @@
"id": "grid + icon.a",
"type": "rectangle",
"pos": {
"x": 489,
"x": 498,
"y": 132
},
"width": 53,
@ -365,7 +365,7 @@
"id": "grid + icon.b",
"type": "rectangle",
"pos": {
"x": 582,
"x": 591,
"y": 132
},
"width": 54,
@ -406,7 +406,7 @@
"id": "grid + icon.c",
"type": "rectangle",
"pos": {
"x": 489,
"x": 498,
"y": 238
},
"width": 53,
@ -447,7 +447,7 @@
"id": "grid + icon.d",
"type": "rectangle",
"pos": {
"x": 582,
"x": 591,
"y": 238
},
"width": 54,
@ -491,7 +491,7 @@
"2x2"
],
"pos": {
"x": 762,
"x": 779,
"y": 10
},
"width": 433,
@ -544,7 +544,7 @@
"id": "grid + icon w/ container.a",
"type": "rectangle",
"pos": {
"x": 856,
"x": 873,
"y": 84
},
"width": 53,
@ -585,7 +585,7 @@
"id": "grid + icon w/ container.b",
"type": "rectangle",
"pos": {
"x": 949,
"x": 966,
"y": 120
},
"width": 151,
@ -626,7 +626,7 @@
"id": "grid + icon w/ container.b.b child",
"type": "rectangle",
"pos": {
"x": 979,
"x": 996,
"y": 150
},
"width": 91,
@ -667,7 +667,7 @@
"id": "grid + icon w/ container.c",
"type": "rectangle",
"pos": {
"x": 856,
"x": 873,
"y": 286
},
"width": 53,
@ -708,7 +708,7 @@
"id": "grid + icon w/ container.d",
"type": "rectangle",
"pos": {
"x": 949,
"x": 966,
"y": 286
},
"width": 151,
@ -752,7 +752,7 @@
"2x2"
],
"pos": {
"x": 1255,
"x": 1272,
"y": 0
},
"width": 364,
@ -785,7 +785,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
@ -793,7 +792,7 @@
"id": "no label grid w/ container + icon.a",
"type": "rectangle",
"pos": {
"x": 1315,
"x": 1332,
"y": 60
},
"width": 53,
@ -834,7 +833,7 @@
"id": "no label grid w/ container + icon.b",
"type": "rectangle",
"pos": {
"x": 1408,
"x": 1425,
"y": 129
},
"width": 151,
@ -887,7 +886,7 @@
"id": "no label grid w/ container + icon.b.b child",
"type": "rectangle",
"pos": {
"x": 1438,
"x": 1455,
"y": 159
},
"width": 91,
@ -928,7 +927,7 @@
"id": "no label grid w/ container + icon.c",
"type": "rectangle",
"pos": {
"x": 1315,
"x": 1332,
"y": 295
},
"width": 53,
@ -969,7 +968,7 @@
"id": "no label grid w/ container + icon.d",
"type": "rectangle",
"pos": {
"x": 1408,
"x": 1425,
"y": 295
},
"width": 151,
@ -1013,7 +1012,7 @@
"2x2"
],
"pos": {
"x": 1679,
"x": 1696,
"y": 58
},
"width": 267,
@ -1058,7 +1057,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
@ -1066,7 +1064,7 @@
"id": "no label grid + icon.a",
"type": "rectangle",
"pos": {
"x": 1739,
"x": 1756,
"y": 132
},
"width": 53,
@ -1107,7 +1105,7 @@
"id": "no label grid + icon.b",
"type": "rectangle",
"pos": {
"x": 1832,
"x": 1849,
"y": 132
},
"width": 54,
@ -1148,7 +1146,7 @@
"id": "no label grid + icon.c",
"type": "rectangle",
"pos": {
"x": 1739,
"x": 1756,
"y": 238
},
"width": 53,
@ -1189,7 +1187,7 @@
"id": "no label grid + icon.d",
"type": "rectangle",
"pos": {
"x": 1832,
"x": 1849,
"y": 238
},
"width": 54,

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View file

@ -274,7 +274,7 @@
"x": 436,
"y": 62
},
"width": 278,
"width": 295,
"height": 347,
"opacity": 1,
"strokeDash": 0,
@ -324,7 +324,7 @@
"id": "grid + icon.a",
"type": "rectangle",
"pos": {
"x": 501,
"x": 510,
"y": 136
},
"width": 53,
@ -365,7 +365,7 @@
"id": "grid + icon.b",
"type": "rectangle",
"pos": {
"x": 594,
"x": 603,
"y": 136
},
"width": 54,
@ -406,7 +406,7 @@
"id": "grid + icon.c",
"type": "rectangle",
"pos": {
"x": 501,
"x": 510,
"y": 242
},
"width": 53,
@ -447,7 +447,7 @@
"id": "grid + icon.d",
"type": "rectangle",
"pos": {
"x": 594,
"x": 603,
"y": 242
},
"width": 54,
@ -491,7 +491,7 @@
"2x2"
],
"pos": {
"x": 734,
"x": 751,
"y": 12
},
"width": 433,
@ -544,7 +544,7 @@
"id": "grid + icon w/ container.a",
"type": "rectangle",
"pos": {
"x": 808,
"x": 825,
"y": 86
},
"width": 53,
@ -585,7 +585,7 @@
"id": "grid + icon w/ container.b",
"type": "rectangle",
"pos": {
"x": 901,
"x": 918,
"y": 86
},
"width": 191,
@ -626,7 +626,7 @@
"id": "grid + icon w/ container.b.b child",
"type": "rectangle",
"pos": {
"x": 951,
"x": 968,
"y": 136
},
"width": 91,
@ -667,7 +667,7 @@
"id": "grid + icon w/ container.c",
"type": "rectangle",
"pos": {
"x": 808,
"x": 825,
"y": 292
},
"width": 53,
@ -708,7 +708,7 @@
"id": "grid + icon w/ container.d",
"type": "rectangle",
"pos": {
"x": 901,
"x": 918,
"y": 292
},
"width": 191,
@ -752,7 +752,7 @@
"2x2"
],
"pos": {
"x": 1187,
"x": 1204,
"y": 27
},
"width": 404,
@ -785,7 +785,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
@ -793,7 +792,7 @@
"id": "no label grid w/ container + icon.a",
"type": "rectangle",
"pos": {
"x": 1247,
"x": 1264,
"y": 87
},
"width": 53,
@ -834,7 +833,7 @@
"id": "no label grid w/ container + icon.b",
"type": "rectangle",
"pos": {
"x": 1340,
"x": 1357,
"y": 87
},
"width": 191,
@ -887,7 +886,7 @@
"id": "no label grid w/ container + icon.b.b child",
"type": "rectangle",
"pos": {
"x": 1390,
"x": 1407,
"y": 161
},
"width": 91,
@ -928,7 +927,7 @@
"id": "no label grid w/ container + icon.c",
"type": "rectangle",
"pos": {
"x": 1247,
"x": 1264,
"y": 317
},
"width": 53,
@ -969,7 +968,7 @@
"id": "no label grid w/ container + icon.d",
"type": "rectangle",
"pos": {
"x": 1340,
"x": 1357,
"y": 317
},
"width": 191,
@ -1013,7 +1012,7 @@
"2x2"
],
"pos": {
"x": 1611,
"x": 1628,
"y": 82
},
"width": 267,
@ -1058,7 +1057,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
@ -1066,7 +1064,7 @@
"id": "no label grid + icon.a",
"type": "rectangle",
"pos": {
"x": 1671,
"x": 1688,
"y": 156
},
"width": 53,
@ -1107,7 +1105,7 @@
"id": "no label grid + icon.b",
"type": "rectangle",
"pos": {
"x": 1764,
"x": 1781,
"y": 156
},
"width": 54,
@ -1148,7 +1146,7 @@
"id": "no label grid + icon.c",
"type": "rectangle",
"pos": {
"x": 1671,
"x": 1688,
"y": 262
},
"width": 53,
@ -1189,7 +1187,7 @@
"id": "no label grid + icon.d",
"type": "rectangle",
"pos": {
"x": 1764,
"x": 1781,
"y": 262
},
"width": 54,

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 106 KiB

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 106 KiB

View file

@ -163,7 +163,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},

View file

@ -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.6.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 402 368"><svg id="d2-svg" class="d2-616560491" width="402" height="368" viewBox="-1 -1 402 368"><rect x="-1.000000" y="-1.000000" width="402.000000" height="368.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
.d2-616560491 .text {
font-family: "d2-616560491-font-regular";
<?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.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 402 368"><svg id="d2-svg" class="d2-2272637932" width="402" height="368" viewBox="-1 -1 402 368"><rect x="-1.000000" y="-1.000000" width="402.000000" height="368.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
.d2-2272637932 .text {
font-family: "d2-2272637932-font-regular";
}
@font-face {
font-family: d2-616560491-font-regular;
font-family: d2-2272637932-font-regular;
src: url("data:application/font-woff;base64,d09GRgABAAAAAAv0AAoAAAAAEmwAAguFAAAAAAAAAAAAAAAAAAAAAAAAAABPUy8yAAAA9AAAAGAAAABgXd/Vo2NtYXAAAAFUAAAAlgAAAL4C4wQRZ2x5ZgAAAewAAAWkAAAHTKtVOSVoZWFkAAAHkAAAADYAAAA2G4Ue32hoZWEAAAfIAAAAJAAAACQKhAXdaG10eAAAB+wAAABsAAAAbDCyBW1sb2NhAAAIWAAAADgAAAA4F+wZwG1heHAAAAiQAAAAIAAAACAAMwD2bmFtZQAACLAAAAMjAAAIFAbDVU1wb3N0AAAL1AAAAB0AAAAg/9EAMgADAgkBkAAFAAACigJYAAAASwKKAlgAAAFeADIBIwAAAgsFAwMEAwICBGAAAvcAAAADAAAAAAAAAABBREJPAEAAIP//Au7/BgAAA9gBESAAAZ8AAAAAAeYClAAAACAAA3icdM09SsMAAIbhJyb+R43/Dg6ewRuI4uSoBwgiKAQFF68jiuhsty5tj9Kl1/gKoWvf9RleFEoFapV/nGuUahcuXblx686D1pMXnTcfCQu97vVe69Gzzqv3JNPMMsk4owwzyF9+85PvfOWz/yyvcGZFqbJqzboNm7Zsq+3Ytaex78ChI8dOnDIHAAD//wEAAP//RkYk3wAAeJxsVVtsI1cZ/s/xxBMnTp1Zezy249vMJDO2k9iJx/bk4oyb+BI3Nzv2Rttc1dAQp7tsQYlKFQisSlq2AiGMtEgr0QIPfamEqBBSAPWtoiXQqhISonSFVisezEqLBLL8gFabMZqxE22AtyP5+P+/2/kGOmAVAMfxHTCACSxwBWgAiWKpAVYUeVKWZJlnDLKIKHIV/VWtIvRcjEgkiNGZRzOHt26h57+J75x9aeL1SuXDrVdfVb9Xe6hG0acPAYMBAHtwFUxAAVhJSRQEkTcaDVbJyos8+bHvQ98Vfy9h8d+7v3V/VflnCn15Z0e+OT5+U13D1bOvnJ4CACCINRu4D78FHoAOThDisURCitoZUhB4zmikbXa7FE3IjNGISqXX5hdeLyc33MOumZCyKUXXlcicLyx+wbx898b1u6VRf8LNTX+1VDqcCXCx4SgAYFgDwDFchU4Np0RJUTttM/KiFE3EYwLPr71z98dv/3Bl/uDg4GAeV9996+2fZ757dPSGjm0NAP1J56hpRrO0RPPUGvqa+vnjx7iau59T713c+wRXoUPfQLH0Whn5cPXsV7Mtjm4A9ARXgdR+5+MszVN/+wg9+AjP5XJnJ60715oNHMZVzR9dB0qiWtwT+tFoROn0DaUczA4O5YJF5bo5cfQSek39RmFdENYL6Fi99dJRArCmJ/oFqoML+gEYThNUjulikqIuLU3xmlFiNCHHdYE/mFr+/o+owUBozuPnXpxYLWZIA7ds5xX+cDtqfm66uEL5xni/bdwevLmu/nnCHZrhfLctyUhwABCEmw30HqprHP+/f+f2XXl2Lzl9QxnJOkN0xDOUFctpbsLezxbNyf1iaT/JMQmrI7IyVq54bLKH1byLNBvoc3wKVvCfc9GHi3HpnIQcv1j07/WXJ7flkOInyhnS4F5wPpv0jXvFlJAzv3FYOFC8rvL7Z2Pj7mA2rbqZSHns2ouAdfx/QHVwgO8SA9pmJNmL8BnYmLYGMdPXldSOvPlFhNVfd1zL8ZN9Hl/hY0SkxqVl89R+obivHO31OE2LGzSVsHmRMLdY0P0tAaDP8CnY9IzQ5LkXlD6YpEolA78YXZwtDY0MTA7g0w922Mj2pvoJCmYUYUD9KTSbkAWAX+ITLIATAIzgOmplp9RswF/wKVhaKunRaQN/NxwsPWMiSLK7024ej+PdsztWCiGFIM4xoXobEyP9D6YMaeCXLkChWo6/jKmt379QHSzQd0k/TUAtZHF9Fm2zI8tkJZWqTCZ3U6ndZGpxMaUsLbW9T+6XivvJTKV8dW/varlyrtcWqrdfbQtbO1UtYM580MP0mm0WX9qJas+HE115gogqartX3M0GOkZ1COmaiLJuZTwmCGIYx2NPZVSrGMaLNbh/jG3xQX9mcGSElfq4mdBqYXjJHXAm/OFB70gfnxkOFsyiW3aywz4nx3T1sPHgZMHPxKyOkJvx0N09rBwWZwL6fkezgbL4ZWDanvBxWZb0Arnw5tHSVH6hK3t8zIZ6vOZeW8S8lkc9Ssebb6bV+vCoiVDIbn3WfLOBPkU1zadL/lLtZ/BgMV8eHBEmOU0XbsG8vYli6mcZRRxEq6prITACCMwA6HeoBj0AkkGy2u2apLJVMrz/3spGN9NNdDNdG8s/QzX1H/15ns/3I5vq0ngA4BNUA/a//vfUBN7Q6n7S8JPbV/Odz5BEZ69pvrhgojqJTgs5u/StnZzJYiI6e7syqKb+nUtzXJpDzqdOLtTBZwYGsrz6BJDWXOi3+DtaoqS4glsROq8vm9GoPR6JDrzw7VxyKpBxRwLryupu+pUF15jzN6Mv/OAVSc4N+yND8cpK8uu3C5iYbeUJ3kE17buldXSphGoav+bv8RzI+AS6ASi9YVo7HD6fw+Hz4TmP0+H1Opwe+A8AAAD//wEAAP//jtuNPQABAAAAAguFoorxJV8PPPUAAwPoAAAAANhdoKEAAAAA3WYvNv46/tsIbwPIAAAAAwACAAAAAAAAAAEAAAPY/u8AAAiY/jr+OghvAAEAAAAAAAAAAAAAAAAAAAAbAo0AWQDIAAACIAADAjsANAJnAFoB7gBaAeYAWgIYABwChQBXAfgANAHIAC4CKwAvAfAALgIgAFIA9gBFAP8AUgIjAFICHgAuAVsAUgGjABwBUgAYAiAASwHTAAwCzgAYAfEAJAD2AFIAAP/JAAAALAAsAFAAgACeALIAwgDUAPgBMAFeAZABxAHmAfICDgIwAlwCfAK8AuIDBAMgA1oDhAOQA6YAAQAAABsAjAAMAGYABwABAAAAAAAAAAAAAAAAAAQAA3icnJTdThtXFIU/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");
}
.d2-616560491 .text-bold {
font-family: "d2-616560491-font-bold";
.d2-2272637932 .text-bold {
font-family: "d2-2272637932-font-bold";
}
@font-face {
font-family: d2-616560491-font-bold;
font-family: d2-2272637932-font-bold;
src: url("data:application/font-woff;base64,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");
}]]></style><style type="text/css"><![CDATA[.shape {
shape-rendering: geometricPrecision;
@ -25,78 +25,78 @@
opacity: 0.5;
}
.d2-616560491 .fill-N1{fill:#0A0F25;}
.d2-616560491 .fill-N2{fill:#676C7E;}
.d2-616560491 .fill-N3{fill:#9499AB;}
.d2-616560491 .fill-N4{fill:#CFD2DD;}
.d2-616560491 .fill-N5{fill:#DEE1EB;}
.d2-616560491 .fill-N6{fill:#EEF1F8;}
.d2-616560491 .fill-N7{fill:#FFFFFF;}
.d2-616560491 .fill-B1{fill:#0D32B2;}
.d2-616560491 .fill-B2{fill:#0D32B2;}
.d2-616560491 .fill-B3{fill:#E3E9FD;}
.d2-616560491 .fill-B4{fill:#E3E9FD;}
.d2-616560491 .fill-B5{fill:#EDF0FD;}
.d2-616560491 .fill-B6{fill:#F7F8FE;}
.d2-616560491 .fill-AA2{fill:#4A6FF3;}
.d2-616560491 .fill-AA4{fill:#EDF0FD;}
.d2-616560491 .fill-AA5{fill:#F7F8FE;}
.d2-616560491 .fill-AB4{fill:#EDF0FD;}
.d2-616560491 .fill-AB5{fill:#F7F8FE;}
.d2-616560491 .stroke-N1{stroke:#0A0F25;}
.d2-616560491 .stroke-N2{stroke:#676C7E;}
.d2-616560491 .stroke-N3{stroke:#9499AB;}
.d2-616560491 .stroke-N4{stroke:#CFD2DD;}
.d2-616560491 .stroke-N5{stroke:#DEE1EB;}
.d2-616560491 .stroke-N6{stroke:#EEF1F8;}
.d2-616560491 .stroke-N7{stroke:#FFFFFF;}
.d2-616560491 .stroke-B1{stroke:#0D32B2;}
.d2-616560491 .stroke-B2{stroke:#0D32B2;}
.d2-616560491 .stroke-B3{stroke:#E3E9FD;}
.d2-616560491 .stroke-B4{stroke:#E3E9FD;}
.d2-616560491 .stroke-B5{stroke:#EDF0FD;}
.d2-616560491 .stroke-B6{stroke:#F7F8FE;}
.d2-616560491 .stroke-AA2{stroke:#4A6FF3;}
.d2-616560491 .stroke-AA4{stroke:#EDF0FD;}
.d2-616560491 .stroke-AA5{stroke:#F7F8FE;}
.d2-616560491 .stroke-AB4{stroke:#EDF0FD;}
.d2-616560491 .stroke-AB5{stroke:#F7F8FE;}
.d2-616560491 .background-color-N1{background-color:#0A0F25;}
.d2-616560491 .background-color-N2{background-color:#676C7E;}
.d2-616560491 .background-color-N3{background-color:#9499AB;}
.d2-616560491 .background-color-N4{background-color:#CFD2DD;}
.d2-616560491 .background-color-N5{background-color:#DEE1EB;}
.d2-616560491 .background-color-N6{background-color:#EEF1F8;}
.d2-616560491 .background-color-N7{background-color:#FFFFFF;}
.d2-616560491 .background-color-B1{background-color:#0D32B2;}
.d2-616560491 .background-color-B2{background-color:#0D32B2;}
.d2-616560491 .background-color-B3{background-color:#E3E9FD;}
.d2-616560491 .background-color-B4{background-color:#E3E9FD;}
.d2-616560491 .background-color-B5{background-color:#EDF0FD;}
.d2-616560491 .background-color-B6{background-color:#F7F8FE;}
.d2-616560491 .background-color-AA2{background-color:#4A6FF3;}
.d2-616560491 .background-color-AA4{background-color:#EDF0FD;}
.d2-616560491 .background-color-AA5{background-color:#F7F8FE;}
.d2-616560491 .background-color-AB4{background-color:#EDF0FD;}
.d2-616560491 .background-color-AB5{background-color:#F7F8FE;}
.d2-616560491 .color-N1{color:#0A0F25;}
.d2-616560491 .color-N2{color:#676C7E;}
.d2-616560491 .color-N3{color:#9499AB;}
.d2-616560491 .color-N4{color:#CFD2DD;}
.d2-616560491 .color-N5{color:#DEE1EB;}
.d2-616560491 .color-N6{color:#EEF1F8;}
.d2-616560491 .color-N7{color:#FFFFFF;}
.d2-616560491 .color-B1{color:#0D32B2;}
.d2-616560491 .color-B2{color:#0D32B2;}
.d2-616560491 .color-B3{color:#E3E9FD;}
.d2-616560491 .color-B4{color:#E3E9FD;}
.d2-616560491 .color-B5{color:#EDF0FD;}
.d2-616560491 .color-B6{color:#F7F8FE;}
.d2-616560491 .color-AA2{color:#4A6FF3;}
.d2-616560491 .color-AA4{color:#EDF0FD;}
.d2-616560491 .color-AA5{color:#F7F8FE;}
.d2-616560491 .color-AB4{color:#EDF0FD;}
.d2-616560491 .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="The Universe"><g class="shape" ><rect x="0.000000" y="0.000000" width="400.000000" height="366.000000" class=" stroke-B1 fill-B4" style="stroke-width:2;" /></g><text x="200.000000" y="33.000000" class="text fill-N1" style="text-anchor:middle;font-size:28px">The Universe</text></g><g id="The Universe.FirstTwo"><g class="shape" ><rect x="0.000000" y="46.000000" width="300.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="150.000000" y="84.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">FirstTwo</text></g><g id="The Universe.Last"><g class="shape" ><rect x="300.000000" y="46.000000" width="100.000000" height="66.000000" fill="red" class=" stroke-B1" style="stroke-width:2;" /></g><text x="350.000000" y="84.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Last</text></g><g id="The Universe.TALA"><g class="shape" ><rect x="0.000000" y="112.000000" width="100.000000" height="193.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g></g><g id="The Universe.D2"><g class="shape" ><rect x="100.000000" y="112.000000" width="200.000000" height="193.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="200.000000" y="214.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">D2</text></g><g id="The Universe.Cloud"><g class="shape" ><rect x="300.000000" y="112.000000" width="100.000000" height="193.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="350.000000" y="214.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Cloud</text></g><g id="The Universe.Terrastruct"><g class="shape" ><rect x="0.000000" y="305.000000" width="400.000000" height="61.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="200.000000" y="341.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Terrastruct</text></g><g id="The Universe.TALA.TALA"><g class="shape" ><rect x="0.000000" y="112.000000" width="100.000000" height="61.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="50.000000" y="148.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">TALA</text></g><g id="The Universe.TALA.D2"><g class="shape" ><rect x="0.000000" y="173.000000" width="100.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="50.000000" y="211.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">D2</text></g><g id="The Universe.TALA.Cloud"><g class="shape" ><rect x="0.000000" y="239.000000" width="100.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="50.000000" y="277.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Cloud</text></g><mask id="d2-616560491" maskUnits="userSpaceOnUse" x="-1" y="-1" width="402" height="368">
.d2-2272637932 .fill-N1{fill:#0A0F25;}
.d2-2272637932 .fill-N2{fill:#676C7E;}
.d2-2272637932 .fill-N3{fill:#9499AB;}
.d2-2272637932 .fill-N4{fill:#CFD2DD;}
.d2-2272637932 .fill-N5{fill:#DEE1EB;}
.d2-2272637932 .fill-N6{fill:#EEF1F8;}
.d2-2272637932 .fill-N7{fill:#FFFFFF;}
.d2-2272637932 .fill-B1{fill:#0D32B2;}
.d2-2272637932 .fill-B2{fill:#0D32B2;}
.d2-2272637932 .fill-B3{fill:#E3E9FD;}
.d2-2272637932 .fill-B4{fill:#E3E9FD;}
.d2-2272637932 .fill-B5{fill:#EDF0FD;}
.d2-2272637932 .fill-B6{fill:#F7F8FE;}
.d2-2272637932 .fill-AA2{fill:#4A6FF3;}
.d2-2272637932 .fill-AA4{fill:#EDF0FD;}
.d2-2272637932 .fill-AA5{fill:#F7F8FE;}
.d2-2272637932 .fill-AB4{fill:#EDF0FD;}
.d2-2272637932 .fill-AB5{fill:#F7F8FE;}
.d2-2272637932 .stroke-N1{stroke:#0A0F25;}
.d2-2272637932 .stroke-N2{stroke:#676C7E;}
.d2-2272637932 .stroke-N3{stroke:#9499AB;}
.d2-2272637932 .stroke-N4{stroke:#CFD2DD;}
.d2-2272637932 .stroke-N5{stroke:#DEE1EB;}
.d2-2272637932 .stroke-N6{stroke:#EEF1F8;}
.d2-2272637932 .stroke-N7{stroke:#FFFFFF;}
.d2-2272637932 .stroke-B1{stroke:#0D32B2;}
.d2-2272637932 .stroke-B2{stroke:#0D32B2;}
.d2-2272637932 .stroke-B3{stroke:#E3E9FD;}
.d2-2272637932 .stroke-B4{stroke:#E3E9FD;}
.d2-2272637932 .stroke-B5{stroke:#EDF0FD;}
.d2-2272637932 .stroke-B6{stroke:#F7F8FE;}
.d2-2272637932 .stroke-AA2{stroke:#4A6FF3;}
.d2-2272637932 .stroke-AA4{stroke:#EDF0FD;}
.d2-2272637932 .stroke-AA5{stroke:#F7F8FE;}
.d2-2272637932 .stroke-AB4{stroke:#EDF0FD;}
.d2-2272637932 .stroke-AB5{stroke:#F7F8FE;}
.d2-2272637932 .background-color-N1{background-color:#0A0F25;}
.d2-2272637932 .background-color-N2{background-color:#676C7E;}
.d2-2272637932 .background-color-N3{background-color:#9499AB;}
.d2-2272637932 .background-color-N4{background-color:#CFD2DD;}
.d2-2272637932 .background-color-N5{background-color:#DEE1EB;}
.d2-2272637932 .background-color-N6{background-color:#EEF1F8;}
.d2-2272637932 .background-color-N7{background-color:#FFFFFF;}
.d2-2272637932 .background-color-B1{background-color:#0D32B2;}
.d2-2272637932 .background-color-B2{background-color:#0D32B2;}
.d2-2272637932 .background-color-B3{background-color:#E3E9FD;}
.d2-2272637932 .background-color-B4{background-color:#E3E9FD;}
.d2-2272637932 .background-color-B5{background-color:#EDF0FD;}
.d2-2272637932 .background-color-B6{background-color:#F7F8FE;}
.d2-2272637932 .background-color-AA2{background-color:#4A6FF3;}
.d2-2272637932 .background-color-AA4{background-color:#EDF0FD;}
.d2-2272637932 .background-color-AA5{background-color:#F7F8FE;}
.d2-2272637932 .background-color-AB4{background-color:#EDF0FD;}
.d2-2272637932 .background-color-AB5{background-color:#F7F8FE;}
.d2-2272637932 .color-N1{color:#0A0F25;}
.d2-2272637932 .color-N2{color:#676C7E;}
.d2-2272637932 .color-N3{color:#9499AB;}
.d2-2272637932 .color-N4{color:#CFD2DD;}
.d2-2272637932 .color-N5{color:#DEE1EB;}
.d2-2272637932 .color-N6{color:#EEF1F8;}
.d2-2272637932 .color-N7{color:#FFFFFF;}
.d2-2272637932 .color-B1{color:#0D32B2;}
.d2-2272637932 .color-B2{color:#0D32B2;}
.d2-2272637932 .color-B3{color:#E3E9FD;}
.d2-2272637932 .color-B4{color:#E3E9FD;}
.d2-2272637932 .color-B5{color:#EDF0FD;}
.d2-2272637932 .color-B6{color:#F7F8FE;}
.d2-2272637932 .color-AA2{color:#4A6FF3;}
.d2-2272637932 .color-AA4{color:#EDF0FD;}
.d2-2272637932 .color-AA5{color:#F7F8FE;}
.d2-2272637932 .color-AB4{color:#EDF0FD;}
.d2-2272637932 .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="The Universe"><g class="shape" ><rect x="0.000000" y="0.000000" width="400.000000" height="366.000000" class=" stroke-B1 fill-B4" style="stroke-width:2;" /></g><text x="200.000000" y="33.000000" class="text fill-N1" style="text-anchor:middle;font-size:28px">The Universe</text></g><g id="The Universe.FirstTwo"><g class="shape" ><rect x="0.000000" y="46.000000" width="300.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="150.000000" y="84.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">FirstTwo</text></g><g id="The Universe.Last"><g class="shape" ><rect x="300.000000" y="46.000000" width="100.000000" height="66.000000" fill="red" class=" stroke-B1" style="stroke-width:2;" /></g><text x="350.000000" y="84.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Last</text></g><g id="The Universe.TALA"><g class="shape" ><rect x="0.000000" y="112.000000" width="100.000000" height="193.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g></g><g id="The Universe.D2"><g class="shape" ><rect x="100.000000" y="112.000000" width="200.000000" height="193.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="200.000000" y="214.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">D2</text></g><g id="The Universe.Cloud"><g class="shape" ><rect x="300.000000" y="112.000000" width="100.000000" height="193.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="350.000000" y="214.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Cloud</text></g><g id="The Universe.Terrastruct"><g class="shape" ><rect x="0.000000" y="305.000000" width="400.000000" height="61.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="200.000000" y="341.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Terrastruct</text></g><g id="The Universe.TALA.TALA"><g class="shape" ><rect x="0.000000" y="112.000000" width="100.000000" height="61.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="50.000000" y="148.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">TALA</text></g><g id="The Universe.TALA.D2"><g class="shape" ><rect x="0.000000" y="173.000000" width="100.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="50.000000" y="211.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">D2</text></g><g id="The Universe.TALA.Cloud"><g class="shape" ><rect x="0.000000" y="239.000000" width="100.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="50.000000" y="277.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Cloud</text></g><mask id="d2-2272637932" maskUnits="userSpaceOnUse" x="-1" y="-1" width="402" height="368">
<rect x="-1" y="-1" width="402" height="368" fill="white"></rect>
<rect x="124.000000" y="5.000000" width="152" height="36" fill="rgba(0,0,0,0.75)"></rect>
<rect x="119.000000" y="68.500000" width="62" height="21" fill="rgba(0,0,0,0.75)"></rect>

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -163,7 +163,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},

View file

@ -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.6.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 402 368"><svg id="d2-svg" class="d2-974195226" width="402" height="368" viewBox="11 11 402 368"><rect x="11.000000" y="11.000000" width="402.000000" height="368.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
.d2-974195226 .text {
font-family: "d2-974195226-font-regular";
<?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.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 402 368"><svg id="d2-svg" class="d2-1702214057" width="402" height="368" viewBox="11 11 402 368"><rect x="11.000000" y="11.000000" width="402.000000" height="368.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
.d2-1702214057 .text {
font-family: "d2-1702214057-font-regular";
}
@font-face {
font-family: d2-974195226-font-regular;
font-family: d2-1702214057-font-regular;
src: url("data:application/font-woff;base64,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");
}
.d2-974195226 .text-bold {
font-family: "d2-974195226-font-bold";
.d2-1702214057 .text-bold {
font-family: "d2-1702214057-font-bold";
}
@font-face {
font-family: d2-974195226-font-bold;
font-family: d2-1702214057-font-bold;
src: url("data:application/font-woff;base64,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");
}]]></style><style type="text/css"><![CDATA[.shape {
shape-rendering: geometricPrecision;
@ -25,78 +25,78 @@
opacity: 0.5;
}
.d2-974195226 .fill-N1{fill:#0A0F25;}
.d2-974195226 .fill-N2{fill:#676C7E;}
.d2-974195226 .fill-N3{fill:#9499AB;}
.d2-974195226 .fill-N4{fill:#CFD2DD;}
.d2-974195226 .fill-N5{fill:#DEE1EB;}
.d2-974195226 .fill-N6{fill:#EEF1F8;}
.d2-974195226 .fill-N7{fill:#FFFFFF;}
.d2-974195226 .fill-B1{fill:#0D32B2;}
.d2-974195226 .fill-B2{fill:#0D32B2;}
.d2-974195226 .fill-B3{fill:#E3E9FD;}
.d2-974195226 .fill-B4{fill:#E3E9FD;}
.d2-974195226 .fill-B5{fill:#EDF0FD;}
.d2-974195226 .fill-B6{fill:#F7F8FE;}
.d2-974195226 .fill-AA2{fill:#4A6FF3;}
.d2-974195226 .fill-AA4{fill:#EDF0FD;}
.d2-974195226 .fill-AA5{fill:#F7F8FE;}
.d2-974195226 .fill-AB4{fill:#EDF0FD;}
.d2-974195226 .fill-AB5{fill:#F7F8FE;}
.d2-974195226 .stroke-N1{stroke:#0A0F25;}
.d2-974195226 .stroke-N2{stroke:#676C7E;}
.d2-974195226 .stroke-N3{stroke:#9499AB;}
.d2-974195226 .stroke-N4{stroke:#CFD2DD;}
.d2-974195226 .stroke-N5{stroke:#DEE1EB;}
.d2-974195226 .stroke-N6{stroke:#EEF1F8;}
.d2-974195226 .stroke-N7{stroke:#FFFFFF;}
.d2-974195226 .stroke-B1{stroke:#0D32B2;}
.d2-974195226 .stroke-B2{stroke:#0D32B2;}
.d2-974195226 .stroke-B3{stroke:#E3E9FD;}
.d2-974195226 .stroke-B4{stroke:#E3E9FD;}
.d2-974195226 .stroke-B5{stroke:#EDF0FD;}
.d2-974195226 .stroke-B6{stroke:#F7F8FE;}
.d2-974195226 .stroke-AA2{stroke:#4A6FF3;}
.d2-974195226 .stroke-AA4{stroke:#EDF0FD;}
.d2-974195226 .stroke-AA5{stroke:#F7F8FE;}
.d2-974195226 .stroke-AB4{stroke:#EDF0FD;}
.d2-974195226 .stroke-AB5{stroke:#F7F8FE;}
.d2-974195226 .background-color-N1{background-color:#0A0F25;}
.d2-974195226 .background-color-N2{background-color:#676C7E;}
.d2-974195226 .background-color-N3{background-color:#9499AB;}
.d2-974195226 .background-color-N4{background-color:#CFD2DD;}
.d2-974195226 .background-color-N5{background-color:#DEE1EB;}
.d2-974195226 .background-color-N6{background-color:#EEF1F8;}
.d2-974195226 .background-color-N7{background-color:#FFFFFF;}
.d2-974195226 .background-color-B1{background-color:#0D32B2;}
.d2-974195226 .background-color-B2{background-color:#0D32B2;}
.d2-974195226 .background-color-B3{background-color:#E3E9FD;}
.d2-974195226 .background-color-B4{background-color:#E3E9FD;}
.d2-974195226 .background-color-B5{background-color:#EDF0FD;}
.d2-974195226 .background-color-B6{background-color:#F7F8FE;}
.d2-974195226 .background-color-AA2{background-color:#4A6FF3;}
.d2-974195226 .background-color-AA4{background-color:#EDF0FD;}
.d2-974195226 .background-color-AA5{background-color:#F7F8FE;}
.d2-974195226 .background-color-AB4{background-color:#EDF0FD;}
.d2-974195226 .background-color-AB5{background-color:#F7F8FE;}
.d2-974195226 .color-N1{color:#0A0F25;}
.d2-974195226 .color-N2{color:#676C7E;}
.d2-974195226 .color-N3{color:#9499AB;}
.d2-974195226 .color-N4{color:#CFD2DD;}
.d2-974195226 .color-N5{color:#DEE1EB;}
.d2-974195226 .color-N6{color:#EEF1F8;}
.d2-974195226 .color-N7{color:#FFFFFF;}
.d2-974195226 .color-B1{color:#0D32B2;}
.d2-974195226 .color-B2{color:#0D32B2;}
.d2-974195226 .color-B3{color:#E3E9FD;}
.d2-974195226 .color-B4{color:#E3E9FD;}
.d2-974195226 .color-B5{color:#EDF0FD;}
.d2-974195226 .color-B6{color:#F7F8FE;}
.d2-974195226 .color-AA2{color:#4A6FF3;}
.d2-974195226 .color-AA4{color:#EDF0FD;}
.d2-974195226 .color-AA5{color:#F7F8FE;}
.d2-974195226 .color-AB4{color:#EDF0FD;}
.d2-974195226 .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="The Universe"><g class="shape" ><rect x="12.000000" y="12.000000" width="400.000000" height="366.000000" class=" stroke-B1 fill-B4" style="stroke-width:2;" /></g><text x="212.000000" y="45.000000" class="text fill-N1" style="text-anchor:middle;font-size:28px">The Universe</text></g><g id="The Universe.FirstTwo"><g class="shape" ><rect x="12.000000" y="58.000000" width="300.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="162.000000" y="96.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">FirstTwo</text></g><g id="The Universe.Last"><g class="shape" ><rect x="312.000000" y="58.000000" width="100.000000" height="66.000000" fill="red" class=" stroke-B1" style="stroke-width:2;" /></g><text x="362.000000" y="96.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Last</text></g><g id="The Universe.TALA"><g class="shape" ><rect x="12.000000" y="124.000000" width="100.000000" height="193.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g></g><g id="The Universe.D2"><g class="shape" ><rect x="112.000000" y="124.000000" width="200.000000" height="193.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="212.000000" y="226.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">D2</text></g><g id="The Universe.Cloud"><g class="shape" ><rect x="312.000000" y="124.000000" width="100.000000" height="193.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="362.000000" y="226.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Cloud</text></g><g id="The Universe.Terrastruct"><g class="shape" ><rect x="12.000000" y="317.000000" width="400.000000" height="61.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="212.000000" y="353.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Terrastruct</text></g><g id="The Universe.TALA.TALA"><g class="shape" ><rect x="12.000000" y="124.000000" width="100.000000" height="61.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="62.000000" y="160.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">TALA</text></g><g id="The Universe.TALA.D2"><g class="shape" ><rect x="12.000000" y="185.000000" width="100.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="62.000000" y="223.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">D2</text></g><g id="The Universe.TALA.Cloud"><g class="shape" ><rect x="12.000000" y="251.000000" width="100.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="62.000000" y="289.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Cloud</text></g><mask id="d2-974195226" maskUnits="userSpaceOnUse" x="11" y="11" width="402" height="368">
.d2-1702214057 .fill-N1{fill:#0A0F25;}
.d2-1702214057 .fill-N2{fill:#676C7E;}
.d2-1702214057 .fill-N3{fill:#9499AB;}
.d2-1702214057 .fill-N4{fill:#CFD2DD;}
.d2-1702214057 .fill-N5{fill:#DEE1EB;}
.d2-1702214057 .fill-N6{fill:#EEF1F8;}
.d2-1702214057 .fill-N7{fill:#FFFFFF;}
.d2-1702214057 .fill-B1{fill:#0D32B2;}
.d2-1702214057 .fill-B2{fill:#0D32B2;}
.d2-1702214057 .fill-B3{fill:#E3E9FD;}
.d2-1702214057 .fill-B4{fill:#E3E9FD;}
.d2-1702214057 .fill-B5{fill:#EDF0FD;}
.d2-1702214057 .fill-B6{fill:#F7F8FE;}
.d2-1702214057 .fill-AA2{fill:#4A6FF3;}
.d2-1702214057 .fill-AA4{fill:#EDF0FD;}
.d2-1702214057 .fill-AA5{fill:#F7F8FE;}
.d2-1702214057 .fill-AB4{fill:#EDF0FD;}
.d2-1702214057 .fill-AB5{fill:#F7F8FE;}
.d2-1702214057 .stroke-N1{stroke:#0A0F25;}
.d2-1702214057 .stroke-N2{stroke:#676C7E;}
.d2-1702214057 .stroke-N3{stroke:#9499AB;}
.d2-1702214057 .stroke-N4{stroke:#CFD2DD;}
.d2-1702214057 .stroke-N5{stroke:#DEE1EB;}
.d2-1702214057 .stroke-N6{stroke:#EEF1F8;}
.d2-1702214057 .stroke-N7{stroke:#FFFFFF;}
.d2-1702214057 .stroke-B1{stroke:#0D32B2;}
.d2-1702214057 .stroke-B2{stroke:#0D32B2;}
.d2-1702214057 .stroke-B3{stroke:#E3E9FD;}
.d2-1702214057 .stroke-B4{stroke:#E3E9FD;}
.d2-1702214057 .stroke-B5{stroke:#EDF0FD;}
.d2-1702214057 .stroke-B6{stroke:#F7F8FE;}
.d2-1702214057 .stroke-AA2{stroke:#4A6FF3;}
.d2-1702214057 .stroke-AA4{stroke:#EDF0FD;}
.d2-1702214057 .stroke-AA5{stroke:#F7F8FE;}
.d2-1702214057 .stroke-AB4{stroke:#EDF0FD;}
.d2-1702214057 .stroke-AB5{stroke:#F7F8FE;}
.d2-1702214057 .background-color-N1{background-color:#0A0F25;}
.d2-1702214057 .background-color-N2{background-color:#676C7E;}
.d2-1702214057 .background-color-N3{background-color:#9499AB;}
.d2-1702214057 .background-color-N4{background-color:#CFD2DD;}
.d2-1702214057 .background-color-N5{background-color:#DEE1EB;}
.d2-1702214057 .background-color-N6{background-color:#EEF1F8;}
.d2-1702214057 .background-color-N7{background-color:#FFFFFF;}
.d2-1702214057 .background-color-B1{background-color:#0D32B2;}
.d2-1702214057 .background-color-B2{background-color:#0D32B2;}
.d2-1702214057 .background-color-B3{background-color:#E3E9FD;}
.d2-1702214057 .background-color-B4{background-color:#E3E9FD;}
.d2-1702214057 .background-color-B5{background-color:#EDF0FD;}
.d2-1702214057 .background-color-B6{background-color:#F7F8FE;}
.d2-1702214057 .background-color-AA2{background-color:#4A6FF3;}
.d2-1702214057 .background-color-AA4{background-color:#EDF0FD;}
.d2-1702214057 .background-color-AA5{background-color:#F7F8FE;}
.d2-1702214057 .background-color-AB4{background-color:#EDF0FD;}
.d2-1702214057 .background-color-AB5{background-color:#F7F8FE;}
.d2-1702214057 .color-N1{color:#0A0F25;}
.d2-1702214057 .color-N2{color:#676C7E;}
.d2-1702214057 .color-N3{color:#9499AB;}
.d2-1702214057 .color-N4{color:#CFD2DD;}
.d2-1702214057 .color-N5{color:#DEE1EB;}
.d2-1702214057 .color-N6{color:#EEF1F8;}
.d2-1702214057 .color-N7{color:#FFFFFF;}
.d2-1702214057 .color-B1{color:#0D32B2;}
.d2-1702214057 .color-B2{color:#0D32B2;}
.d2-1702214057 .color-B3{color:#E3E9FD;}
.d2-1702214057 .color-B4{color:#E3E9FD;}
.d2-1702214057 .color-B5{color:#EDF0FD;}
.d2-1702214057 .color-B6{color:#F7F8FE;}
.d2-1702214057 .color-AA2{color:#4A6FF3;}
.d2-1702214057 .color-AA4{color:#EDF0FD;}
.d2-1702214057 .color-AA5{color:#F7F8FE;}
.d2-1702214057 .color-AB4{color:#EDF0FD;}
.d2-1702214057 .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="The Universe"><g class="shape" ><rect x="12.000000" y="12.000000" width="400.000000" height="366.000000" class=" stroke-B1 fill-B4" style="stroke-width:2;" /></g><text x="212.000000" y="45.000000" class="text fill-N1" style="text-anchor:middle;font-size:28px">The Universe</text></g><g id="The Universe.FirstTwo"><g class="shape" ><rect x="12.000000" y="58.000000" width="300.000000" height="66.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="162.000000" y="96.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">FirstTwo</text></g><g id="The Universe.Last"><g class="shape" ><rect x="312.000000" y="58.000000" width="100.000000" height="66.000000" fill="red" class=" stroke-B1" style="stroke-width:2;" /></g><text x="362.000000" y="96.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Last</text></g><g id="The Universe.TALA"><g class="shape" ><rect x="12.000000" y="124.000000" width="100.000000" height="193.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g></g><g id="The Universe.D2"><g class="shape" ><rect x="112.000000" y="124.000000" width="200.000000" height="193.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="212.000000" y="226.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">D2</text></g><g id="The Universe.Cloud"><g class="shape" ><rect x="312.000000" y="124.000000" width="100.000000" height="193.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="362.000000" y="226.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Cloud</text></g><g id="The Universe.Terrastruct"><g class="shape" ><rect x="12.000000" y="317.000000" width="400.000000" height="61.000000" class=" stroke-B1 fill-B5" style="stroke-width:2;" /></g><text x="212.000000" y="353.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Terrastruct</text></g><g id="The Universe.TALA.TALA"><g class="shape" ><rect x="12.000000" y="124.000000" width="100.000000" height="61.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="62.000000" y="160.000000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">TALA</text></g><g id="The Universe.TALA.D2"><g class="shape" ><rect x="12.000000" y="185.000000" width="100.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="62.000000" y="223.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">D2</text></g><g id="The Universe.TALA.Cloud"><g class="shape" ><rect x="12.000000" y="251.000000" width="100.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="62.000000" y="289.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">Cloud</text></g><mask id="d2-1702214057" maskUnits="userSpaceOnUse" x="11" y="11" width="402" height="368">
<rect x="11" y="11" width="402" height="368" fill="white"></rect>
<rect x="136.000000" y="17.000000" width="152" height="36" fill="rgba(0,0,0,0.75)"></rect>
<rect x="131.000000" y="80.500000" width="62" height="21" fill="rgba(0,0,0,0.75)"></rect>

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -2499,7 +2499,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 116 KiB

View file

@ -2499,7 +2499,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 117 KiB

View file

@ -922,7 +922,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View file

@ -922,7 +922,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View file

@ -40,7 +40,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
@ -81,7 +80,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View file

@ -40,7 +40,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
@ -81,7 +80,6 @@
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View file

@ -0,0 +1,307 @@
{
"name": "",
"isFolderOnly": false,
"fontFamily": "SourceSansPro",
"shapes": [
{
"id": "A",
"type": "rectangle",
"pos": {
"x": 0,
"y": 0
},
"width": 56,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "A",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 11,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "B",
"type": "rectangle",
"pos": {
"x": 208,
"y": 0
},
"width": 54,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "B",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 9,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "C",
"type": "rectangle",
"pos": {
"x": 1,
"y": 126
},
"width": 54,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "C",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 9,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "D",
"type": "rectangle",
"pos": {
"x": 208,
"y": 126
},
"width": 54,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "D",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 9,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
}
],
"connections": [
{
"id": "(A <-> B)[0]",
"src": "A",
"srcArrow": "unfilled-triangle",
"dst": "B",
"dstArrow": "unfilled-triangle",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "default",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 48,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 56,
"y": 33
},
{
"x": 116.80000305175781,
"y": 33
},
{
"x": 147.1999969482422,
"y": 33
},
{
"x": 208,
"y": 33
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(C <-> D)[0]",
"src": "C",
"srcArrow": "unfilled-triangle",
"dst": "D",
"dstArrow": "unfilled-triangle",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "triangle",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 52,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 55,
"y": 159
},
{
"x": 116.5999984741211,
"y": 159
},
{
"x": 147.1999969482422,
"y": 159
},
{
"x": 208,
"y": 159
}
],
"isCurve": true,
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
}
],
"root": {
"id": "",
"type": "",
"pos": {
"x": 0,
"y": 0
},
"width": 0,
"height": 0,
"opacity": 0,
"strokeDash": 0,
"strokeWidth": 0,
"borderRadius": 0,
"fill": "N7",
"stroke": "",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "",
"fontSize": 0,
"fontFamily": "",
"language": "",
"color": "",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"zIndex": 0,
"level": 0
}
}

View file

@ -0,0 +1,107 @@
<?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.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 264 194"><svg id="d2-svg" class="d2-857282650" width="264" height="194" viewBox="-1 -1 264 194"><rect x="-1.000000" y="-1.000000" width="264.000000" height="194.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
.d2-857282650 .text-bold {
font-family: "d2-857282650-font-bold";
}
@font-face {
font-family: d2-857282650-font-bold;
src: url("data:application/font-woff;base64,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");
}
.d2-857282650 .text-italic {
font-family: "d2-857282650-font-italic";
}
@font-face {
font-family: d2-857282650-font-italic;
src: url("data:application/font-woff;base64,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");
}]]></style><style type="text/css"><![CDATA[.shape {
shape-rendering: geometricPrecision;
stroke-linejoin: round;
}
.connection {
stroke-linecap: round;
stroke-linejoin: round;
}
.blend {
mix-blend-mode: multiply;
opacity: 0.5;
}
.d2-857282650 .fill-N1{fill:#0A0F25;}
.d2-857282650 .fill-N2{fill:#676C7E;}
.d2-857282650 .fill-N3{fill:#9499AB;}
.d2-857282650 .fill-N4{fill:#CFD2DD;}
.d2-857282650 .fill-N5{fill:#DEE1EB;}
.d2-857282650 .fill-N6{fill:#EEF1F8;}
.d2-857282650 .fill-N7{fill:#FFFFFF;}
.d2-857282650 .fill-B1{fill:#0D32B2;}
.d2-857282650 .fill-B2{fill:#0D32B2;}
.d2-857282650 .fill-B3{fill:#E3E9FD;}
.d2-857282650 .fill-B4{fill:#E3E9FD;}
.d2-857282650 .fill-B5{fill:#EDF0FD;}
.d2-857282650 .fill-B6{fill:#F7F8FE;}
.d2-857282650 .fill-AA2{fill:#4A6FF3;}
.d2-857282650 .fill-AA4{fill:#EDF0FD;}
.d2-857282650 .fill-AA5{fill:#F7F8FE;}
.d2-857282650 .fill-AB4{fill:#EDF0FD;}
.d2-857282650 .fill-AB5{fill:#F7F8FE;}
.d2-857282650 .stroke-N1{stroke:#0A0F25;}
.d2-857282650 .stroke-N2{stroke:#676C7E;}
.d2-857282650 .stroke-N3{stroke:#9499AB;}
.d2-857282650 .stroke-N4{stroke:#CFD2DD;}
.d2-857282650 .stroke-N5{stroke:#DEE1EB;}
.d2-857282650 .stroke-N6{stroke:#EEF1F8;}
.d2-857282650 .stroke-N7{stroke:#FFFFFF;}
.d2-857282650 .stroke-B1{stroke:#0D32B2;}
.d2-857282650 .stroke-B2{stroke:#0D32B2;}
.d2-857282650 .stroke-B3{stroke:#E3E9FD;}
.d2-857282650 .stroke-B4{stroke:#E3E9FD;}
.d2-857282650 .stroke-B5{stroke:#EDF0FD;}
.d2-857282650 .stroke-B6{stroke:#F7F8FE;}
.d2-857282650 .stroke-AA2{stroke:#4A6FF3;}
.d2-857282650 .stroke-AA4{stroke:#EDF0FD;}
.d2-857282650 .stroke-AA5{stroke:#F7F8FE;}
.d2-857282650 .stroke-AB4{stroke:#EDF0FD;}
.d2-857282650 .stroke-AB5{stroke:#F7F8FE;}
.d2-857282650 .background-color-N1{background-color:#0A0F25;}
.d2-857282650 .background-color-N2{background-color:#676C7E;}
.d2-857282650 .background-color-N3{background-color:#9499AB;}
.d2-857282650 .background-color-N4{background-color:#CFD2DD;}
.d2-857282650 .background-color-N5{background-color:#DEE1EB;}
.d2-857282650 .background-color-N6{background-color:#EEF1F8;}
.d2-857282650 .background-color-N7{background-color:#FFFFFF;}
.d2-857282650 .background-color-B1{background-color:#0D32B2;}
.d2-857282650 .background-color-B2{background-color:#0D32B2;}
.d2-857282650 .background-color-B3{background-color:#E3E9FD;}
.d2-857282650 .background-color-B4{background-color:#E3E9FD;}
.d2-857282650 .background-color-B5{background-color:#EDF0FD;}
.d2-857282650 .background-color-B6{background-color:#F7F8FE;}
.d2-857282650 .background-color-AA2{background-color:#4A6FF3;}
.d2-857282650 .background-color-AA4{background-color:#EDF0FD;}
.d2-857282650 .background-color-AA5{background-color:#F7F8FE;}
.d2-857282650 .background-color-AB4{background-color:#EDF0FD;}
.d2-857282650 .background-color-AB5{background-color:#F7F8FE;}
.d2-857282650 .color-N1{color:#0A0F25;}
.d2-857282650 .color-N2{color:#676C7E;}
.d2-857282650 .color-N3{color:#9499AB;}
.d2-857282650 .color-N4{color:#CFD2DD;}
.d2-857282650 .color-N5{color:#DEE1EB;}
.d2-857282650 .color-N6{color:#EEF1F8;}
.d2-857282650 .color-N7{color:#FFFFFF;}
.d2-857282650 .color-B1{color:#0D32B2;}
.d2-857282650 .color-B2{color:#0D32B2;}
.d2-857282650 .color-B3{color:#E3E9FD;}
.d2-857282650 .color-B4{color:#E3E9FD;}
.d2-857282650 .color-B5{color:#EDF0FD;}
.d2-857282650 .color-B6{color:#F7F8FE;}
.d2-857282650 .color-AA2{color:#4A6FF3;}
.d2-857282650 .color-AA4{color:#EDF0FD;}
.d2-857282650 .color-AA5{color:#F7F8FE;}
.d2-857282650 .color-AB4{color:#EDF0FD;}
.d2-857282650 .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="A"><g class="shape" ><rect x="0.000000" y="0.000000" width="56.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="28.000000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">A</text></g><g id="B"><g class="shape" ><rect x="208.000000" y="0.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="235.000000" y="38.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">B</text></g><g id="C"><g class="shape" ><rect x="1.000000" y="126.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="28.000000" y="164.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">C</text></g><g id="D"><g class="shape" ><rect x="208.000000" y="126.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="235.000000" y="164.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">D</text></g><g id="(A &lt;-&gt; B)[0]"><marker id="mk-2954633863" markerWidth="13.000000" markerHeight="15.000000" refX="3.000000" refY="7.500000" viewBox="0.000000 0.000000 13.000000 15.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="12.000000,1.000000 1.000000,7.500000 12.000000,14.000000" class="connection stroke-B1 fill-N7" stroke-width="2" /> </marker><marker id="mk-3777340426" markerWidth="13.000000" markerHeight="15.000000" refX="10.000000" refY="7.500000" viewBox="0.000000 0.000000 13.000000 15.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="1.000000,1.000000 12.000000,7.500000 1.000000,14.000000" class="connection stroke-B1 fill-N7" stroke-width="2" /> </marker><path d="M 60.000000 33.000000 C 116.800003 33.000000 147.199997 33.000000 204.000000 33.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-start="url(#mk-2954633863)" marker-end="url(#mk-3777340426)" mask="url(#d2-857282650)" /><text x="132.000000" y="39.000000" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px">default</text></g><g id="(C &lt;-&gt; D)[0]"><path d="M 59.000000 159.000000 C 116.599998 159.000000 147.199997 159.000000 204.000000 159.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-start="url(#mk-2954633863)" marker-end="url(#mk-3777340426)" mask="url(#d2-857282650)" /><text x="132.000000" y="165.000000" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px">triangle</text></g><mask id="d2-857282650" maskUnits="userSpaceOnUse" x="-1" y="-1" width="264" height="194">
<rect x="-1" y="-1" width="264" height="194" fill="white"></rect>
<rect x="22.500000" y="22.500000" width="11" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="230.500000" y="22.500000" width="9" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="23.500000" y="148.500000" width="9" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="230.500000" y="148.500000" width="9" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="108.000000" y="23.000000" width="48" height="21" fill="black"></rect>
<rect x="106.000000" y="149.000000" width="52" height="21" fill="black"></rect>
</mask></svg></svg>

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,289 @@
{
"name": "",
"isFolderOnly": false,
"fontFamily": "SourceSansPro",
"shapes": [
{
"id": "A",
"type": "rectangle",
"pos": {
"x": 12,
"y": 12
},
"width": 56,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "A",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 11,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "B",
"type": "rectangle",
"pos": {
"x": 260,
"y": 12
},
"width": 54,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "B",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 9,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "C",
"type": "rectangle",
"pos": {
"x": 14,
"y": 98
},
"width": 54,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "C",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 9,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "D",
"type": "rectangle",
"pos": {
"x": 260,
"y": 98
},
"width": 54,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B6",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "D",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 9,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
}
],
"connections": [
{
"id": "(A <-> B)[0]",
"src": "A",
"srcArrow": "unfilled-triangle",
"dst": "B",
"dstArrow": "unfilled-triangle",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "default",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 48,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 68,
"y": 45
},
{
"x": 260,
"y": 45
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(C <-> D)[0]",
"src": "C",
"srcArrow": "unfilled-triangle",
"dst": "D",
"dstArrow": "unfilled-triangle",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"borderRadius": 10,
"label": "triangle",
"fontSize": 16,
"fontFamily": "DEFAULT",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 52,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 68,
"y": 131
},
{
"x": 260,
"y": 131
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
}
],
"root": {
"id": "",
"type": "",
"pos": {
"x": 0,
"y": 0
},
"width": 0,
"height": 0,
"opacity": 0,
"strokeDash": 0,
"strokeWidth": 0,
"borderRadius": 0,
"fill": "N7",
"stroke": "",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "",
"fontSize": 0,
"fontFamily": "",
"language": "",
"color": "",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"zIndex": 0,
"level": 0
}
}

View file

@ -0,0 +1,107 @@
<?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.1-HEAD" preserveAspectRatio="xMinYMin meet" viewBox="0 0 304 154"><svg id="d2-svg" class="d2-3330864888" width="304" height="154" viewBox="11 11 304 154"><rect x="11.000000" y="11.000000" width="304.000000" height="154.000000" rx="0.000000" class=" fill-N7" stroke-width="0" /><style type="text/css"><![CDATA[
.d2-3330864888 .text-bold {
font-family: "d2-3330864888-font-bold";
}
@font-face {
font-family: d2-3330864888-font-bold;
src: url("data:application/font-woff;base64,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");
}
.d2-3330864888 .text-italic {
font-family: "d2-3330864888-font-italic";
}
@font-face {
font-family: d2-3330864888-font-italic;
src: url("data:application/font-woff;base64,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");
}]]></style><style type="text/css"><![CDATA[.shape {
shape-rendering: geometricPrecision;
stroke-linejoin: round;
}
.connection {
stroke-linecap: round;
stroke-linejoin: round;
}
.blend {
mix-blend-mode: multiply;
opacity: 0.5;
}
.d2-3330864888 .fill-N1{fill:#0A0F25;}
.d2-3330864888 .fill-N2{fill:#676C7E;}
.d2-3330864888 .fill-N3{fill:#9499AB;}
.d2-3330864888 .fill-N4{fill:#CFD2DD;}
.d2-3330864888 .fill-N5{fill:#DEE1EB;}
.d2-3330864888 .fill-N6{fill:#EEF1F8;}
.d2-3330864888 .fill-N7{fill:#FFFFFF;}
.d2-3330864888 .fill-B1{fill:#0D32B2;}
.d2-3330864888 .fill-B2{fill:#0D32B2;}
.d2-3330864888 .fill-B3{fill:#E3E9FD;}
.d2-3330864888 .fill-B4{fill:#E3E9FD;}
.d2-3330864888 .fill-B5{fill:#EDF0FD;}
.d2-3330864888 .fill-B6{fill:#F7F8FE;}
.d2-3330864888 .fill-AA2{fill:#4A6FF3;}
.d2-3330864888 .fill-AA4{fill:#EDF0FD;}
.d2-3330864888 .fill-AA5{fill:#F7F8FE;}
.d2-3330864888 .fill-AB4{fill:#EDF0FD;}
.d2-3330864888 .fill-AB5{fill:#F7F8FE;}
.d2-3330864888 .stroke-N1{stroke:#0A0F25;}
.d2-3330864888 .stroke-N2{stroke:#676C7E;}
.d2-3330864888 .stroke-N3{stroke:#9499AB;}
.d2-3330864888 .stroke-N4{stroke:#CFD2DD;}
.d2-3330864888 .stroke-N5{stroke:#DEE1EB;}
.d2-3330864888 .stroke-N6{stroke:#EEF1F8;}
.d2-3330864888 .stroke-N7{stroke:#FFFFFF;}
.d2-3330864888 .stroke-B1{stroke:#0D32B2;}
.d2-3330864888 .stroke-B2{stroke:#0D32B2;}
.d2-3330864888 .stroke-B3{stroke:#E3E9FD;}
.d2-3330864888 .stroke-B4{stroke:#E3E9FD;}
.d2-3330864888 .stroke-B5{stroke:#EDF0FD;}
.d2-3330864888 .stroke-B6{stroke:#F7F8FE;}
.d2-3330864888 .stroke-AA2{stroke:#4A6FF3;}
.d2-3330864888 .stroke-AA4{stroke:#EDF0FD;}
.d2-3330864888 .stroke-AA5{stroke:#F7F8FE;}
.d2-3330864888 .stroke-AB4{stroke:#EDF0FD;}
.d2-3330864888 .stroke-AB5{stroke:#F7F8FE;}
.d2-3330864888 .background-color-N1{background-color:#0A0F25;}
.d2-3330864888 .background-color-N2{background-color:#676C7E;}
.d2-3330864888 .background-color-N3{background-color:#9499AB;}
.d2-3330864888 .background-color-N4{background-color:#CFD2DD;}
.d2-3330864888 .background-color-N5{background-color:#DEE1EB;}
.d2-3330864888 .background-color-N6{background-color:#EEF1F8;}
.d2-3330864888 .background-color-N7{background-color:#FFFFFF;}
.d2-3330864888 .background-color-B1{background-color:#0D32B2;}
.d2-3330864888 .background-color-B2{background-color:#0D32B2;}
.d2-3330864888 .background-color-B3{background-color:#E3E9FD;}
.d2-3330864888 .background-color-B4{background-color:#E3E9FD;}
.d2-3330864888 .background-color-B5{background-color:#EDF0FD;}
.d2-3330864888 .background-color-B6{background-color:#F7F8FE;}
.d2-3330864888 .background-color-AA2{background-color:#4A6FF3;}
.d2-3330864888 .background-color-AA4{background-color:#EDF0FD;}
.d2-3330864888 .background-color-AA5{background-color:#F7F8FE;}
.d2-3330864888 .background-color-AB4{background-color:#EDF0FD;}
.d2-3330864888 .background-color-AB5{background-color:#F7F8FE;}
.d2-3330864888 .color-N1{color:#0A0F25;}
.d2-3330864888 .color-N2{color:#676C7E;}
.d2-3330864888 .color-N3{color:#9499AB;}
.d2-3330864888 .color-N4{color:#CFD2DD;}
.d2-3330864888 .color-N5{color:#DEE1EB;}
.d2-3330864888 .color-N6{color:#EEF1F8;}
.d2-3330864888 .color-N7{color:#FFFFFF;}
.d2-3330864888 .color-B1{color:#0D32B2;}
.d2-3330864888 .color-B2{color:#0D32B2;}
.d2-3330864888 .color-B3{color:#E3E9FD;}
.d2-3330864888 .color-B4{color:#E3E9FD;}
.d2-3330864888 .color-B5{color:#EDF0FD;}
.d2-3330864888 .color-B6{color:#F7F8FE;}
.d2-3330864888 .color-AA2{color:#4A6FF3;}
.d2-3330864888 .color-AA4{color:#EDF0FD;}
.d2-3330864888 .color-AA5{color:#F7F8FE;}
.d2-3330864888 .color-AB4{color:#EDF0FD;}
.d2-3330864888 .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="A"><g class="shape" ><rect x="12.000000" y="12.000000" width="56.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="40.000000" y="50.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">A</text></g><g id="B"><g class="shape" ><rect x="260.000000" y="12.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="287.000000" y="50.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">B</text></g><g id="C"><g class="shape" ><rect x="14.000000" y="98.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="41.000000" y="136.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">C</text></g><g id="D"><g class="shape" ><rect x="260.000000" y="98.000000" width="54.000000" height="66.000000" class=" stroke-B1 fill-B6" style="stroke-width:2;" /></g><text x="287.000000" y="136.500000" class="text-bold fill-N1" style="text-anchor:middle;font-size:16px">D</text></g><g id="(A &lt;-&gt; B)[0]"><marker id="mk-2954633863" markerWidth="13.000000" markerHeight="15.000000" refX="3.000000" refY="7.500000" viewBox="0.000000 0.000000 13.000000 15.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="12.000000,1.000000 1.000000,7.500000 12.000000,14.000000" class="connection stroke-B1 fill-N7" stroke-width="2" /> </marker><marker id="mk-3777340426" markerWidth="13.000000" markerHeight="15.000000" refX="10.000000" refY="7.500000" viewBox="0.000000 0.000000 13.000000 15.000000" orient="auto" markerUnits="userSpaceOnUse"> <polygon points="1.000000,1.000000 12.000000,7.500000 1.000000,14.000000" class="connection stroke-B1 fill-N7" stroke-width="2" /> </marker><path d="M 72.000000 45.000000 L 256.000000 45.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-start="url(#mk-2954633863)" marker-end="url(#mk-3777340426)" mask="url(#d2-3330864888)" /><text x="164.000000" y="51.000000" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px">default</text></g><g id="(C &lt;-&gt; D)[0]"><path d="M 72.000000 131.000000 L 256.000000 131.000000" fill="none" class="connection stroke-B1" style="stroke-width:2;" marker-start="url(#mk-2954633863)" marker-end="url(#mk-3777340426)" mask="url(#d2-3330864888)" /><text x="164.000000" y="137.000000" class="text-italic fill-N2" style="text-anchor:middle;font-size:16px">triangle</text></g><mask id="d2-3330864888" maskUnits="userSpaceOnUse" x="11" y="11" width="304" height="154">
<rect x="11" y="11" width="304" height="154" fill="white"></rect>
<rect x="34.500000" y="34.500000" width="11" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="282.500000" y="34.500000" width="9" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="36.500000" y="120.500000" width="9" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="282.500000" y="120.500000" width="9" height="21" fill="rgba(0,0,0,0.75)"></rect>
<rect x="140.000000" y="35.000000" width="48" height="21" fill="black"></rect>
<rect x="138.000000" y="121.000000" width="52" height="21" fill="black"></rect>
</mask></svg></svg>

After

Width:  |  Height:  |  Size: 16 KiB

5
go.mod generated
View file

@ -26,8 +26,8 @@ require (
golang.org/x/text v0.8.0
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2
gonum.org/v1/plot v0.12.0
nhooyr.io/websocket v1.8.7
oss.terrastruct.com/util-go v0.0.0-20230604222829-11c3c60fec14
nhooyr.io/websocket v1.8.10
oss.terrastruct.com/util-go v0.0.0-20231101220827-55b3812542c2
)
require (
@ -44,7 +44,6 @@ require (
github.com/go-stack/stack v1.8.1 // indirect
github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/klauspost/compress v1.10.3 // indirect
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect

49
go.sum generated
View file

@ -66,10 +66,6 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs=
@ -78,23 +74,10 @@ github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3Bop
github.com/go-fonts/liberation v0.2.0 h1:jAkAWJP4S+OsrPLZM4/eC9iW7CtHy+HBXrEwZXWo5VM=
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81 h1:6zl3BbBhdnMkpSj2YY30qV3gDcVBGtFgVsV3+/i+mKQ=
github.com/go-pdf/fpdf v0.6.0 h1:MlgtGIfsdMEEQJr2le6b/HNr1ZlQwxyWr77r2aj2U/8=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
@ -108,8 +91,6 @@ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4er
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
@ -125,22 +106,15 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jung-kurt/gofpdf v1.16.2 h1:jgbatWHfRlPYiK85qgevsZTHviWXKwB1TTiKdz5PtRc=
github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0=
github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
@ -149,8 +123,6 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
@ -160,10 +132,6 @@ github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mazznoer/csscolorparser v0.1.3 h1:vug4zh6loQxAUxfU1DZEu70gTPufDPspamZlHAkKcxE=
github.com/mazznoer/csscolorparser v0.1.3/go.mod h1:Aj22+L/rYN/Y6bj3bYqO3N6g1dtdHtGfQ32xZ5PJQic=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
@ -185,16 +153,11 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M=
github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@ -266,14 +229,12 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -316,9 +277,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
@ -327,8 +286,8 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g=
nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
oss.terrastruct.com/util-go v0.0.0-20230604222829-11c3c60fec14 h1:oy5vtt6O2qYxeSpqWhyevrdUenFfuhphixozUlpL6qY=
oss.terrastruct.com/util-go v0.0.0-20230604222829-11c3c60fec14/go.mod h1:eMWv0sOtD9T2RUl90DLWfuShZCYp4NrsqNpI8eqO6U4=
nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q=
nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c=
oss.terrastruct.com/util-go v0.0.0-20231101220827-55b3812542c2 h1:n6y6RoZCgZDchN4gLGlzNRO1Jdf9xOGGqohDBph5BG8=
oss.terrastruct.com/util-go v0.0.0-20231101220827-55b3812542c2/go.mod h1:eMWv0sOtD9T2RUl90DLWfuShZCYp4NrsqNpI8eqO6U4=
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=

View file

@ -14,42 +14,169 @@ const RIGHT_LABEL_POSITION = 3.0 / 4.0
// This is the space between a node border and its outside label
const PADDING = 5
type Position string
type Position int8
const (
OutsideTopLeft Position = "OUTSIDE_TOP_LEFT"
OutsideTopCenter Position = "OUTSIDE_TOP_CENTER"
OutsideTopRight Position = "OUTSIDE_TOP_RIGHT"
Unset Position = iota
OutsideLeftTop Position = "OUTSIDE_LEFT_TOP"
OutsideLeftMiddle Position = "OUTSIDE_LEFT_MIDDLE"
OutsideLeftBottom Position = "OUTSIDE_LEFT_BOTTOM"
OutsideTopLeft
OutsideTopCenter
OutsideTopRight
OutsideRightTop Position = "OUTSIDE_RIGHT_TOP"
OutsideRightMiddle Position = "OUTSIDE_RIGHT_MIDDLE"
OutsideRightBottom Position = "OUTSIDE_RIGHT_BOTTOM"
OutsideLeftTop
OutsideLeftMiddle
OutsideLeftBottom
OutsideBottomLeft Position = "OUTSIDE_BOTTOM_LEFT"
OutsideBottomCenter Position = "OUTSIDE_BOTTOM_CENTER"
OutsideBottomRight Position = "OUTSIDE_BOTTOM_RIGHT"
OutsideRightTop
OutsideRightMiddle
OutsideRightBottom
InsideTopLeft Position = "INSIDE_TOP_LEFT"
InsideTopCenter Position = "INSIDE_TOP_CENTER"
InsideTopRight Position = "INSIDE_TOP_RIGHT"
OutsideBottomLeft
OutsideBottomCenter
OutsideBottomRight
InsideMiddleLeft Position = "INSIDE_MIDDLE_LEFT"
InsideMiddleCenter Position = "INSIDE_MIDDLE_CENTER"
InsideMiddleRight Position = "INSIDE_MIDDLE_RIGHT"
InsideTopLeft
InsideTopCenter
InsideTopRight
InsideBottomLeft Position = "INSIDE_BOTTOM_LEFT"
InsideBottomCenter Position = "INSIDE_BOTTOM_CENTER"
InsideBottomRight Position = "INSIDE_BOTTOM_RIGHT"
InsideMiddleLeft
InsideMiddleCenter
InsideMiddleRight
UnlockedTop Position = "UNLOCKED_TOP"
UnlockedMiddle Position = "UNLOCKED_MIDDLE"
UnlockedBottom Position = "UNLOCKED_BOTTOM"
InsideBottomLeft
InsideBottomCenter
InsideBottomRight
UnlockedTop
UnlockedMiddle
UnlockedBottom
)
func FromString(s string) Position {
switch s {
case "OUTSIDE_TOP_LEFT":
return OutsideTopLeft
case "OUTSIDE_TOP_CENTER":
return OutsideTopCenter
case "OUTSIDE_TOP_RIGHT":
return OutsideTopRight
case "OUTSIDE_LEFT_TOP":
return OutsideLeftTop
case "OUTSIDE_LEFT_MIDDLE":
return OutsideLeftMiddle
case "OUTSIDE_LEFT_BOTTOM":
return OutsideLeftBottom
case "OUTSIDE_RIGHT_TOP":
return OutsideRightTop
case "OUTSIDE_RIGHT_MIDDLE":
return OutsideRightMiddle
case "OUTSIDE_RIGHT_BOTTOM":
return OutsideRightBottom
case "OUTSIDE_BOTTOM_LEFT":
return OutsideBottomLeft
case "OUTSIDE_BOTTOM_CENTER":
return OutsideBottomCenter
case "OUTSIDE_BOTTOM_RIGHT":
return OutsideBottomRight
case "INSIDE_TOP_LEFT":
return InsideTopLeft
case "INSIDE_TOP_CENTER":
return InsideTopCenter
case "INSIDE_TOP_RIGHT":
return InsideTopRight
case "INSIDE_MIDDLE_LEFT":
return InsideMiddleLeft
case "INSIDE_MIDDLE_CENTER":
return InsideMiddleCenter
case "INSIDE_MIDDLE_RIGHT":
return InsideMiddleRight
case "INSIDE_BOTTOM_LEFT":
return InsideBottomLeft
case "INSIDE_BOTTOM_CENTER":
return InsideBottomCenter
case "INSIDE_BOTTOM_RIGHT":
return InsideBottomRight
case "UNLOCKED_TOP":
return UnlockedTop
case "UNLOCKED_MIDDLE":
return UnlockedMiddle
case "UNLOCKED_BOTTOM":
return UnlockedBottom
default:
return Unset
}
}
func (position Position) String() string {
switch position {
case OutsideTopLeft:
return "OUTSIDE_TOP_LEFT"
case OutsideTopCenter:
return "OUTSIDE_TOP_CENTER"
case OutsideTopRight:
return "OUTSIDE_TOP_RIGHT"
case OutsideLeftTop:
return "OUTSIDE_LEFT_TOP"
case OutsideLeftMiddle:
return "OUTSIDE_LEFT_MIDDLE"
case OutsideLeftBottom:
return "OUTSIDE_LEFT_BOTTOM"
case OutsideRightTop:
return "OUTSIDE_RIGHT_TOP"
case OutsideRightMiddle:
return "OUTSIDE_RIGHT_MIDDLE"
case OutsideRightBottom:
return "OUTSIDE_RIGHT_BOTTOM"
case OutsideBottomLeft:
return "OUTSIDE_BOTTOM_LEFT"
case OutsideBottomCenter:
return "OUTSIDE_BOTTOM_CENTER"
case OutsideBottomRight:
return "OUTSIDE_BOTTOM_RIGHT"
case InsideTopLeft:
return "INSIDE_TOP_LEFT"
case InsideTopCenter:
return "INSIDE_TOP_CENTER"
case InsideTopRight:
return "INSIDE_TOP_RIGHT"
case InsideMiddleLeft:
return "INSIDE_MIDDLE_LEFT"
case InsideMiddleCenter:
return "INSIDE_MIDDLE_CENTER"
case InsideMiddleRight:
return "INSIDE_MIDDLE_RIGHT"
case InsideBottomLeft:
return "INSIDE_BOTTOM_LEFT"
case InsideBottomCenter:
return "INSIDE_BOTTOM_CENTER"
case InsideBottomRight:
return "INSIDE_BOTTOM_RIGHT"
case UnlockedTop:
return "UNLOCKED_TOP"
case UnlockedMiddle:
return "UNLOCKED_MIDDLE"
case UnlockedBottom:
return "UNLOCKED_BOTTOM"
default:
return ""
}
}
func (position Position) IsShapePosition() bool {
switch position {
case OutsideTopLeft, OutsideTopCenter, OutsideTopRight,
@ -167,7 +294,7 @@ func (position Position) Mirrored() Position {
return UnlockedMiddle
default:
return ""
return Unset
}
}

View file

@ -1 +1 @@
vFdtb9s4Ev6uXzFwWvQuiN5sN0mFwx1yadMUaLHe2rv9UmBBiyOZrUSqJGUn3ea_L4aSbFl2usUusPkQkzPkzMNnXkhZUSIpoRR3qBMYjeB3DwDvqoJJRmwm8K3kHgDAyQmcni72NpyeOo0Pb-QnTC0wbs5gs1K-VX6mikJtzkDJpWKaC5m3a6-VXKM2zjqUitcFdppaG6WFzM8qlovGf6tabJBZ4MjrqhBpXzNHvUYOnFkGhcrzxtE378GboaoKBC5Mqtao7xMYDUXwURrUa5HiyGO8JeGKN6dzVDiyA0qbBEYnaczG2WTkPXje7lzQmkhg9NNO2LO8zzL4_4UhjiNLDu0fWdRi9lJKSSFRR9sY5ppVqy-FB5CqskRpPQBbGFYJ76G3PmjXJfCaBj-__ShvkKOm6oO51cwquHYl0dBBKZuAKFlOUaMCSGBlbWWSMKyrQjEebMRnUSIXLFA6D2lW0SwkHEqa0K7qchnGYXwRtj5_e6tyFZh1HsbjKKru_KE8qGS-D7s9VJeeJyeUImjD2qAGWofSwuqea5crZ7AWRixFIew9ZKKwSGnmfetbbMhJYPF27l_N3sC_lkjkc6w0pkTHv4_QRkHYD8swSv_x4cCJZzeC2hFklDSLdnKjHWwOTQD3uaWZCSxqzYzVdWqJgdCoVLAijOKJ35r0Y-JrGKmHnkeCfCz-YrZSEmGDy8cXdxzdLhYzuJJcK8G9DS4T-IDLH0bNvtYaww-4fDqO5k1uPx1H19SLw6uq2pO-VCUT0hw_lPemIsxJz_OzzrUxF8H668Y5FCZ0W8Jf0bXvD0JjgcaErKoK9IWz4sdj_3yaL_2q1iR9PoniC7_8JONJUfjsf084Eia_yJ88O4TSkpEcISHlchqQKBPUjB0ialcNO6Epmba-g2B8i-lKqkLlAk34PB6HrLHb6F0ZHLKwwaXLw23UdjloUqVdY-8a90c5d6LRke6WZRyzmLrbSpUImsnPtPeWJu_dxOtZ7pre9kqYP9qmDuAsVlpkFt7Pro-s3nO-Q9m7DROYDsQEP4qiV5OXBP97CFqQzknb8G-3Y-fu5NGmf7z--6b-EtydgSNkDbR_n5zHvLXElEyumLVMwjiBd93Ey8VXuqvTzwm87oZe034cFQnM3aS5dzxLzfi-EpQfbggzgQPXPU8DzdbZQN7zNzxG569tCYN6aKtzIK00cuHejN3pxwnMdsI2of_84ku59F0x-5XMg6xglmau0KmGz6fnsft3Gb9obzKHvqsGl39NWX63KreE9QNTYpmydIUJvGtHR1oQj7-cZxHD4i6qlA7SQtU8o-smkGjDShPZ1i9UrkzIMZrGz6OpH3G-9KeT7NxfXr5AP8NoOo0macouud955e15vAxtukrghn56h-gewQlYXeOOR3rlNtuYrTXSRjf48a1EnpB54oiji_qHdzqolAqt8w6Ey7DGmHckN_6p1GghEJpDEDvw2140DlLdvDz6WbVXYPQoPNRuE2c0GuqOVEb_Ttg2on6L2qkOoSVwzSQXnFlsUmTXSsfbF-tjLeuwIOiP5Dvr-t22fQ-X31zcXN68ouUP9GnDdHG_FJon8IqG_xea3o-1ZXkCv1jm7teKUbjn9NO-n2sprECTwPVu4j14aNMEgiBAmx7w63RdWt92L1Gvn23bAO0J27j0Zc7WjsLmm9F4fwQAAP__
vFdtb9u2E3-vT3FwWvT_D6In201SYdiQpU1ToMW82lvfFBho8SSxoUiVpOwka777QEqyZdnpgg1YXkTkHXn34-8eSBtWImcCoWS3qBIYjeBPDwBvK04EMUyKBL6V1AMAODqC4-PFzobjY6fx4Z34gqkBQvUJrAvpG-lnknO5PgEplpIoykTerr2UYoVKO-tQSlpz7DS10lIxkZ9UJGeN_1a1WCMxQJHWFWdpXzNHtUIKlBgCXOZ54-ib9-DNUFYcgTKdyhWquwRGQxF8FhrViqU48ghtSbigzekcFdrccQwyxnkCo6M0JuNsMvIePG97LmhNJDD6ZSvsWd5lGfwfYYjjwJJ9-wcWtZi9VApDmEAVbWKYK1IVX7kHkMqyRGE8AMM1qZj30FsftOsSeGsHv77_LK6QoiIGKcyNIkbCpeR1KRo6ClJhAqwkuY0aS22GFMZUOgnDuuKS0GDNbliJlJFAqjy0s8rOQotDCh2aoi6XYRzGZ2Hr84_3MpeBXuVhPI6i6tYfyoNK5Luw20N16Xl0ZFMETVhrVGDXoTBQ3FHlcuUEVkyzJePM3EHGuEGbZt63vsWGnAQW7-f-xewd_G-JlnyKlcLU0vH_A7TZIOyGZRilH3zYc-KZNTMGFWQ2aRbt5Eo52BSaAO5ya2c6MKgU0UbVqbEMhFqmjPAwiid-a9KPLV_DSD30PFrIh-LPZoUUCGtcPr644-h6sZjBhaBKMuqtcZnAJ1w-GTW5rxWGn3D5fBzNm9x-Po4uJZcqvKiqHelrWRIm9OFDee8qiznpeX7Rudb6LFjdr51DpkO3JfwdFbuX4hNTyFHrkFQVR585K3489k-n-dKvamWlLydRfOaXX0Q84dwnPz2jaDH5PH_2Yh9KS0ZygISUimlgRRkTFJVDZNtVw06oS6KM7yBo32BaCMllzlCHL-NxSBq7jd6VwT4La1y6PNxEbZuDOpXKNfaucX8WcycaHehuWUYxi213K2SJoIi4sXuv7eSjm3g9y13T21wJ80fb1B6cRaFYZuDj7PLA6h3nW5TaKHmDPiW6SGA6EFv4URS9mby28L-HoAXpnLQN_3ozdu6OHm36h-u_b-ofwd0aOEDWQPvvyXnMW0tMSURBjCECxgl86CZezu7tXZ3eJPC2G3pN-3FUJDB3k-be8YxtxncVs_nhhjBjOHDd8zTQbJwN5D1_w2N0_tqWMKiHtjoH0kohZal7h7SnHycw2wrbhP77iy-lwnfF7FciDzJOjJ25Qrc1fDo9jd2_8_hVe5M59F01uPxryvK7VbkhrB-YEsuUpAUm8KEdHWhBNP56mkUE-W1USRWkXNY0s9dNINCElbJkG5_LXOqQYjSNX0ZTP6J06U8n2am_PH-FfobRdBpN0pScU7_zStvzeBmatEjgyn56hyhrbljFMQGjatzyqA1WzTZiaoV2oxs8faslj4k8ccTZi_rJOx1Umwqt8w6Ey7DGmHcgN_6r1GghWDT7ILbgN71oHKSqeXn0s2qnwOyjcF-7SZzRaKg7UBn9O2HTiPotaqvah5bAJRGUUWKwSZFtKx1vXqyPtaz9grB_Vr61rj5s2vdw-dXZ1fnVG7v8wf60IYrfLZmiCbyxw5-Zsu_H2pA8gd8McfdrRWy45_bTvp9rwQxDncDlduI9eGjSBIIgQJPu8et0XVpfdy9Rr59tmwDtCNu49GXO1pZCLWuVovb-CgAA__8=

View file

@ -5,37 +5,18 @@ import (
"compress/flate"
"encoding/base64"
"io"
"sort"
"strings"
"oss.terrastruct.com/util-go/xdefer"
"oss.terrastruct.com/d2/d2graph"
)
var compressionDict = "->" +
"<-" +
"--" +
"<->"
func init() {
var common []string
for k := range d2graph.ReservedKeywords {
common = append(common, k)
}
sort.Strings(common)
for _, k := range common {
compressionDict += k
}
}
// Encode takes a D2 script and encodes it as a compressed base64 string for embedding in URLs.
func Encode(raw string) (_ string, err error) {
defer xdefer.Errorf(&err, "failed to encode d2 script")
b := &bytes.Buffer{}
zw, err := flate.NewWriterDict(b, flate.DefaultCompression, []byte(compressionDict))
zw, err := flate.NewWriterDict(b, flate.BestCompression, nil)
if err != nil {
return "", err
}
@ -59,7 +40,7 @@ func Decode(encoded string) (_ string, err error) {
return "", err
}
zr := flate.NewReaderDict(bytes.NewReader(b64Decoded), []byte(compressionDict))
zr := flate.NewReaderDict(bytes.NewReader(b64Decoded), nil)
var b bytes.Buffer
if _, err := io.Copy(&b, zr); err != nil {
return "", err

View file

@ -0,0 +1,435 @@
{
"graph": {
"name": "",
"isFolderOnly": false,
"ast": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,0:0:0-10:0:56",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,1:0:1-1:12:13",
"edges": [
{
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,1:0:1-1:12:13",
"src": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,1:0:1-1:4:5",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,1:0:1-1:1:2",
"value": [
{
"string": "*",
"raw_string": "*"
}
],
"pattern": [
"*"
]
}
},
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,1:2:3-1:4:5",
"value": [
{
"string": "*a",
"raw_string": "*a"
}
],
"pattern": [
"*",
"a"
]
}
}
]
},
"src_arrow": "",
"dst": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,1:8:9-1:12:13",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,1:8:9-1:9:10",
"value": [
{
"string": "*",
"raw_string": "*"
}
],
"pattern": [
"*"
]
}
},
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,1:10:11-1:12:13",
"value": [
{
"string": "*b",
"raw_string": "*b"
}
],
"pattern": [
"*",
"b"
]
}
}
]
},
"dst_arrow": ">"
}
],
"primary": {},
"value": {}
}
},
{
"map_key": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,3:0:15-5:1:34",
"key": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,3:0:15-3:11:26",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,3:0:15-3:11:26",
"value": [
{
"string": "container_1",
"raw_string": "container_1"
}
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,3:13:28-5:1:34",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,4:1:31-4:2:32",
"key": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,4:1:31-4:2:32",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,4:1:31-4:2:32",
"value": [
{
"string": "a",
"raw_string": "a"
}
]
}
}
]
},
"primary": {},
"value": {}
}
}
]
}
}
}
},
{
"map_key": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,7:0:36-9:1:55",
"key": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,7:0:36-7:11:47",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,7:0:36-7:11:47",
"value": [
{
"string": "container_2",
"raw_string": "container_2"
}
]
}
}
]
},
"primary": {},
"value": {
"map": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,7:13:49-9:1:55",
"nodes": [
{
"map_key": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,8:1:52-8:2:53",
"key": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,8:1:52-8:2:53",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,8:1:52-8:2:53",
"value": [
{
"string": "b",
"raw_string": "b"
}
]
}
}
]
},
"primary": {},
"value": {}
}
}
]
}
}
}
}
]
},
"root": {
"id": "",
"id_val": "",
"attributes": {
"label": {
"value": ""
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {},
"near_key": null,
"shape": {
"value": ""
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
},
"edges": [
{
"index": 0,
"isCurve": false,
"src_arrow": false,
"dst_arrow": true,
"references": [
{
"map_key_edge_index": 0
}
],
"attributes": {
"label": {
"value": ""
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {},
"near_key": null,
"shape": {
"value": ""
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
}
],
"objects": [
{
"id": "container_1",
"id_val": "container_1",
"references": [
{
"key": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,3:0:15-3:11:26",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,3:0:15-3:11:26",
"value": [
{
"string": "container_1",
"raw_string": "container_1"
}
]
}
}
]
},
"key_path_index": 0,
"map_key_edge_index": -1
}
],
"attributes": {
"label": {
"value": "container_1"
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {},
"near_key": null,
"shape": {
"value": "rectangle"
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
},
{
"id": "a",
"id_val": "a",
"references": [
{
"key": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,4:1:31-4:2:32",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,4:1:31-4:2:32",
"value": [
{
"string": "a",
"raw_string": "a"
}
]
}
}
]
},
"key_path_index": 0,
"map_key_edge_index": -1
}
],
"attributes": {
"label": {
"value": "a"
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {},
"near_key": null,
"shape": {
"value": "rectangle"
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
},
{
"id": "container_2",
"id_val": "container_2",
"references": [
{
"key": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,7:0:36-7:11:47",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,7:0:36-7:11:47",
"value": [
{
"string": "container_2",
"raw_string": "container_2"
}
]
}
}
]
},
"key_path_index": 0,
"map_key_edge_index": -1
}
],
"attributes": {
"label": {
"value": "container_2"
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {},
"near_key": null,
"shape": {
"value": "rectangle"
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
},
{
"id": "b",
"id_val": "b",
"references": [
{
"key": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,8:1:52-8:2:53",
"path": [
{
"unquoted_string": {
"range": "d2/testdata/d2compiler/TestCompile2/globs/creating-node-bug.d2,8:1:52-8:2:53",
"value": [
{
"string": "b",
"raw_string": "b"
}
]
}
}
]
},
"key_path_index": 0,
"map_key_edge_index": -1
}
],
"attributes": {
"label": {
"value": "b"
},
"labelDimensions": {
"width": 0,
"height": 0
},
"style": {},
"near_key": null,
"shape": {
"value": "rectangle"
},
"direction": {
"value": ""
},
"constraint": null
},
"zIndex": 0
}
]
},
"err": null
}