define new theme

This commit is contained in:
Alexander Wang 2023-03-14 10:40:52 -07:00
parent ad4d483bf8
commit 4c8c2b1ab5
No known key found for this signature in database
GPG key ID: D89FA31966BDBECE
19 changed files with 2680 additions and 12 deletions

View file

@ -273,8 +273,9 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, sketc
layout := plugin.Layout
opts := &d2lib.CompileOptions{
Layout: layout,
Ruler: ruler,
Layout: layout,
Ruler: ruler,
ThemeID: themeID,
}
if sketch {
opts.FontFamily = go2.Pointer(d2fonts.HandDrawn)

View file

@ -9,6 +9,7 @@ import (
"oss.terrastruct.com/d2/d2graph"
"oss.terrastruct.com/d2/d2renderers/d2fonts"
"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/d2themes"
"oss.terrastruct.com/d2/lib/color"
)
@ -20,22 +21,25 @@ func Export(ctx context.Context, g *d2graph.Graph, fontFamily *d2fonts.FontFamil
if fontFamily == nil {
fontFamily = go2.Pointer(d2fonts.SourceSansPro)
}
if g.Theme != nil && g.Theme.SpecialRules.Mono {
fontFamily = go2.Pointer(d2fonts.SourceCodePro)
}
diagram.FontFamily = fontFamily
diagram.Shapes = make([]d2target.Shape, len(g.Objects))
for i := range g.Objects {
diagram.Shapes[i] = toShape(g.Objects[i])
diagram.Shapes[i] = toShape(g.Objects[i], g.Theme)
}
diagram.Connections = make([]d2target.Connection, len(g.Edges))
for i := range g.Edges {
diagram.Connections[i] = toConnection(g.Edges[i])
diagram.Connections[i] = toConnection(g.Edges[i], g.Theme)
}
return diagram, nil
}
func applyTheme(shape *d2target.Shape, obj *d2graph.Object) {
func applyTheme(shape *d2target.Shape, obj *d2graph.Object, theme *d2themes.Theme) {
shape.Stroke = obj.GetStroke(shape.StrokeDash)
shape.Fill = obj.GetFill()
if obj.Attributes.Shape.Value == d2target.ShapeText {
@ -46,6 +50,23 @@ func applyTheme(shape *d2target.Shape, obj *d2graph.Object) {
shape.SecondaryAccentColor = color.AA2
shape.NeutralAccentColor = color.N2
}
// Theme options that change more than color
if theme != nil {
if theme.SpecialRules.OuterContainerDoubleBorder {
if obj.Level() == 1 && len(obj.ChildrenArray) > 0 {
shape.DoubleBorder = true
}
}
if theme.SpecialRules.ContainerDots {
if len(obj.ChildrenArray) > 0 {
shape.FillPattern = "dots"
}
}
if theme.SpecialRules.Mono {
shape.FontFamily = "mono"
}
}
}
func applyStyles(shape *d2target.Shape, obj *d2graph.Object) {
@ -102,7 +123,7 @@ func applyStyles(shape *d2target.Shape, obj *d2graph.Object) {
}
}
func toShape(obj *d2graph.Object) d2target.Shape {
func toShape(obj *d2graph.Object, theme *d2themes.Theme) d2target.Shape {
shape := d2target.BaseShape()
shape.SetType(obj.Attributes.Shape.Value)
shape.ID = obj.AbsID()
@ -127,7 +148,7 @@ func toShape(obj *d2graph.Object) d2target.Shape {
}
applyStyles(shape, obj)
applyTheme(shape, obj)
applyTheme(shape, obj, theme)
shape.Color = text.GetColor(shape.Italic)
applyStyles(shape, obj)
@ -168,7 +189,7 @@ func toShape(obj *d2graph.Object) d2target.Shape {
return *shape
}
func toConnection(edge *d2graph.Edge) d2target.Connection {
func toConnection(edge *d2graph.Edge, theme *d2themes.Theme) d2target.Connection {
connection := d2target.BaseConnection()
connection.ID = edge.AbsID()
connection.ZIndex = edge.ZIndex
@ -208,7 +229,9 @@ func toConnection(edge *d2graph.Edge) d2target.Connection {
connection.DstLabel = edge.DstArrowhead.Label.Value
}
}
if theme != nil && theme.SpecialRules.NoCornerRadius {
connection.BorderRadius = 0
}
if edge.Attributes.Style.BorderRadius != nil {
connection.BorderRadius, _ = strconv.ParseFloat(edge.Attributes.Style.BorderRadius.Value, 64)
}
@ -258,6 +281,9 @@ func toConnection(edge *d2graph.Edge) d2target.Connection {
if edge.Attributes.Style.Bold != nil {
connection.Bold, _ = strconv.ParseBool(edge.Attributes.Style.Bold.Value)
}
if theme != nil && theme.SpecialRules.Mono {
connection.FontFamily = "mono"
}
if edge.Attributes.Style.Font != nil {
connection.FontFamily = edge.Attributes.Style.Font.Value
}

View file

@ -17,6 +17,8 @@ import (
"oss.terrastruct.com/d2/d2renderers/d2fonts"
"oss.terrastruct.com/d2/d2renderers/d2latex"
"oss.terrastruct.com/d2/d2target"
"oss.terrastruct.com/d2/d2themes"
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
"oss.terrastruct.com/d2/lib/color"
"oss.terrastruct.com/d2/lib/geo"
"oss.terrastruct.com/d2/lib/shape"
@ -43,6 +45,8 @@ type Graph struct {
Layers []*Graph `json:"layers,omitempty"`
Scenarios []*Graph `json:"scenarios,omitempty"`
Steps []*Graph `json:"steps,omitempty"`
Theme *d2themes.Theme `json:"theme,omitempty"`
}
func NewGraph() *Graph {
@ -1251,6 +1255,11 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
}
}
if g.Theme != nil && g.Theme.SpecialRules.Mono {
tmp := d2fonts.SourceCodePro
fontFamily = &tmp
}
for _, obj := range g.Objects {
obj.Box = &geo.Box{}
@ -1291,6 +1300,10 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
continue
}
if g.Theme != nil && g.Theme.SpecialRules.CapsLock {
obj.Attributes.Label.Value = strings.ToUpper(obj.Attributes.Label.Value)
}
labelDims, err := obj.GetLabelSize(mtexts, ruler, fontFamily)
if err != nil {
return err
@ -1408,6 +1421,10 @@ func (g *Graph) SetDimensions(mtexts []*d2target.MText, ruler *textmeasure.Ruler
continue
}
if g.Theme != nil && g.Theme.SpecialRules.CapsLock {
edge.Attributes.Label.Value = strings.ToUpper(edge.Attributes.Label.Value)
}
dims := GetTextDimensions(mtexts, ruler, edge.Text(), fontFamily)
if dims == nil {
return fmt.Errorf("dimensions for edge label %#v not found", edge.Text())
@ -1650,3 +1667,15 @@ func (obj *Object) IsDescendantOf(ancestor *Object) bool {
}
return obj.Parent.IsDescendantOf(ancestor)
}
// ApplyTheme applies themes on the graph level
// This is different than on the render level, which only changes colors
// A theme applied on the graph level applies special rules that change the graph
func (g *Graph) ApplyTheme(themeID int64) error {
theme := d2themescatalog.Find(themeID)
if theme == (d2themes.Theme{}) {
return fmt.Errorf("theme %d not found", themeID)
}
g.Theme = &theme
return nil
}

View file

@ -22,6 +22,7 @@ type CompileOptions struct {
MeasuredTexts []*d2target.MText
Ruler *textmeasure.Ruler
Layout func(context.Context, *d2graph.Graph) error
ThemeID int64
// FontFamily controls the font family used for all texts that are not the following:
// - code
@ -51,6 +52,11 @@ func Compile(ctx context.Context, input string, opts *CompileOptions) (*d2target
}
func compile(ctx context.Context, g *d2graph.Graph, opts *CompileOptions) (*d2target.Diagram, error) {
err := g.ApplyTheme(opts.ThemeID)
if err != nil {
return nil, err
}
if len(g.Objects) > 0 {
err := g.SetDimensions(opts.MeasuredTexts, opts.Ruler, opts.FontFamily)
if err != nil {

View file

@ -22,6 +22,7 @@ import (
"oss.terrastruct.com/d2/d2lib"
"oss.terrastruct.com/d2/d2renderers/d2fonts"
"oss.terrastruct.com/d2/d2renderers/d2svg"
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
"oss.terrastruct.com/d2/lib/log"
"oss.terrastruct.com/d2/lib/textmeasure"
)
@ -505,6 +506,54 @@ darker: {
style.font-color: "#fff"
style.fill: "#000"
}
`,
},
{
name: "terminal",
themeID: d2themescatalog.Terminal.ID,
script: `network: {
cell tower: {
satellites: {
shape: stored_data
style.multiple: true
}
transmitter
satellites -> transmitter: send
satellites -> transmitter: send
satellites -> transmitter: send
}
online portal: {
ui: { shape: hexagon }
}
data processor: {
storage: {
shape: cylinder
style.multiple: true
}
}
cell tower.transmitter -> data processor.storage: phone logs
}
user: {
shape: person
width: 130
}
user -> network.cell tower: make call
user -> network.online portal.ui: access {
style.stroke-dash: 3
}
api server -> network.online portal.ui: display
api server -> logs: persist
logs: { shape: page; style.multiple: true }
network.data processor -> api server
`,
},
{
@ -1218,6 +1267,7 @@ func run(t *testing.T, tc testCase) {
Ruler: ruler,
Layout: layout,
FontFamily: go2.Pointer(d2fonts.HandDrawn),
ThemeID: tc.themeID,
})
if !tassert.Nil(t, err) {
return

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 355 KiB

View file

@ -136,8 +136,9 @@ func run(t *testing.T, tc testCase) {
}
diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{
Ruler: ruler,
Layout: d2dagrelayout.DefaultLayout,
Ruler: ruler,
Layout: d2dagrelayout.DefaultLayout,
ThemeID: tc.themeID,
})
if !tassert.Nil(t, err) {
return

View file

@ -430,6 +430,7 @@ func run(t *testing.T, tc testCase) {
Ruler: ruler,
Layout: d2dagrelayout.DefaultLayout,
FontFamily: go2.Pointer(d2fonts.HandDrawn),
ThemeID: 200,
})
if !tassert.Nil(t, err) {
return

View file

@ -8,6 +8,16 @@ type Theme struct {
ID int64 `json:"id"`
Name string `json:"name"`
Colors ColorPalette `json:"colors"`
SpecialRules SpecialRules `json:"specialRules,omitempty"`
}
type SpecialRules struct {
Mono bool `json:"mono"`
NoCornerRadius bool `json:"noCornerRadius"`
OuterContainerDoubleBorder bool `json:"outerContainerDoubleBorder"`
ContainerDots bool `json:"containerDots"`
CapsLock bool `json:"capsLock"`
}
func (t *Theme) IsDark() bool {

View file

@ -22,6 +22,7 @@ var LightCatalog = []d2themes.Theme{
EarthTones,
EvergladeGreen,
ButteredToast,
Terminal,
}
var DarkCatalog = []d2themes.Theme{

View file

@ -0,0 +1,42 @@
package d2themescatalog
import "oss.terrastruct.com/d2/d2themes"
var Terminal = d2themes.Theme{
ID: 300,
Name: "Terminal",
Colors: d2themes.ColorPalette{
Neutrals: TerminalNeutral,
B1: "#000410",
B2: "#0000E4",
B3: "#5AA4DC",
B4: "#E7E9EE",
B5: "#F5F6F9",
B6: "#FFFFFF",
AA2: "#008566",
AA4: "#45BBA5",
AA5: "#7ACCBD",
AB4: "#F1C759",
AB5: "#F9E088",
},
SpecialRules: d2themes.SpecialRules{
Mono: true,
NoCornerRadius: true,
OuterContainerDoubleBorder: true,
ContainerDots: true,
CapsLock: true,
},
}
var TerminalNeutral = d2themes.Neutral{
N1: "#000410",
N2: "#0000B8",
N3: "#9499AB",
N4: "#CFD2DD",
N5: "#C3DEF3",
N6: "#EEF1F8",
N7: "#FFFFFF",
}

View file

@ -17,6 +17,7 @@ import (
// Remember to add if err != nil checks in production.
func main() {
graph, _ := d2compiler.Compile("", strings.NewReader("x -> y"), nil)
graph.ApplyTheme(d2themescatalog.NeutralDefault.ID)
ruler, _ := textmeasure.NewRuler()
_ = graph.SetDimensions(nil, ruler, nil)
_ = d2dagrelayout.Layout(context.Background(), graph, nil)

View file

@ -42,6 +42,7 @@ func TestE2E(t *testing.T) {
t.Run("measured", testMeasured)
t.Run("unicode", testUnicode)
t.Run("root", testRoot)
t.Run("themes", testThemes)
}
func testSanity(t *testing.T) {
@ -86,6 +87,7 @@ type testCase struct {
dagreFeatureError string
elkFeatureError string
expErr string
themeID int64
}
func runa(t *testing.T, tcs []testCase) {
@ -163,6 +165,7 @@ func run(t *testing.T, tc testCase) {
Ruler: ruler,
MeasuredTexts: tc.mtexts,
Layout: layout,
ThemeID: tc.themeID,
})
if tc.expErr != "" {
@ -204,7 +207,7 @@ func run(t *testing.T, tc testCase) {
svgBytes, err := d2svg.Render(diagram, &d2svg.RenderOpts{
Pad: 0,
ThemeID: 0,
ThemeID: tc.themeID,
})
assert.Success(t, err)
err = os.MkdirAll(dataPath, 0755)

1104
e2etests/testdata/themes/terminal/dagre/board.exp.json generated vendored Normal file

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: 276 KiB

895
e2etests/testdata/themes/terminal/elk/board.exp.json generated vendored Normal file
View file

@ -0,0 +1,895 @@
{
"name": "",
"isFolderOnly": false,
"fontFamily": "SourceCodePro",
"shapes": [
{
"id": "network",
"type": "rectangle",
"pos": {
"x": 12,
"y": 311
},
"width": 611,
"height": 902,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B4",
"fillPattern": "dots",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": false,
"double-border": true,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "NETWORK",
"fontSize": 28,
"fontFamily": "mono",
"language": "",
"color": "N1",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 116,
"labelHeight": 36,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "network.cell tower",
"type": "rectangle",
"pos": {
"x": 62,
"y": 361
},
"width": 261,
"height": 413,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B5",
"fillPattern": "dots",
"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": "CELL TOWER",
"fontSize": 24,
"fontFamily": "mono",
"language": "",
"color": "N1",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 143,
"labelHeight": 31,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "network.cell tower.satellites",
"type": "stored_data",
"pos": {
"x": 112,
"y": 411
},
"width": 161,
"height": 66,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "AA5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": true,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "SATELLITES",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 96,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 3
},
{
"id": "network.cell tower.transmitter",
"type": "rectangle",
"pos": {
"x": 117,
"y": 658
},
"width": 151,
"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": "TRANSMITTER",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 106,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 3
},
{
"id": "network.online portal",
"type": "rectangle",
"pos": {
"x": 343,
"y": 366
},
"width": 230,
"height": 169,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B5",
"fillPattern": "dots",
"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": "ONLINE PORTAL",
"fontSize": 24,
"fontFamily": "mono",
"language": "",
"color": "N1",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 185,
"labelHeight": 31,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "network.online portal.ui",
"type": "hexagon",
"pos": {
"x": 418,
"y": 416
},
"width": 80,
"height": 69,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "N5",
"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": "UI",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 18,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 3
},
{
"id": "network.data processor",
"type": "rectangle",
"pos": {
"x": 70,
"y": 945
},
"width": 245,
"height": 218,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B5",
"fillPattern": "dots",
"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": "DATA PROCESSOR",
"fontSize": 24,
"fontFamily": "mono",
"language": "",
"color": "N1",
"italic": false,
"bold": false,
"underline": false,
"labelWidth": 200,
"labelHeight": 31,
"labelPosition": "INSIDE_TOP_CENTER",
"zIndex": 0,
"level": 2
},
{
"id": "network.data processor.storage",
"type": "cylinder",
"pos": {
"x": 136,
"y": 995
},
"width": 112,
"height": 118,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "AA5",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": true,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "STORAGE",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 67,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 3
},
{
"id": "user",
"type": "person",
"pos": {
"x": 314,
"y": 12
},
"width": 130,
"height": 87,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "B3",
"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": "USER",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 38,
"labelHeight": 21,
"labelPosition": "OUTSIDE_BOTTOM_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "api server",
"type": "rectangle",
"pos": {
"x": 592,
"y": 59
},
"width": 142,
"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": "API SERVER",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 97,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
},
{
"id": "logs",
"type": "page",
"pos": {
"x": 703,
"y": 311
},
"width": 82,
"height": 87,
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"borderRadius": 0,
"fill": "AB4",
"stroke": "B1",
"shadow": false,
"3d": false,
"multiple": true,
"double-border": false,
"tooltip": "",
"link": "",
"icon": null,
"iconPosition": "",
"blend": false,
"fields": null,
"methods": null,
"columns": null,
"label": "LOGS",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N1",
"italic": false,
"bold": true,
"underline": false,
"labelWidth": 37,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"zIndex": 0,
"level": 1
}
],
"connections": [
{
"id": "network.cell tower.(satellites -> transmitter)[0]",
"src": "network.cell tower.satellites",
"srcArrow": "none",
"srcLabel": "",
"dst": "network.cell tower.transmitter",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"label": "SEND",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 38,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 143,
"y": 477
},
{
"x": 143.5,
"y": 658
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "network.cell tower.(satellites -> transmitter)[1]",
"src": "network.cell tower.satellites",
"srcArrow": "none",
"srcLabel": "",
"dst": "network.cell tower.transmitter",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"label": "SEND",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 38,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 192,
"y": 477
},
{
"x": 192.5,
"y": 658
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "network.cell tower.(satellites -> transmitter)[2]",
"src": "network.cell tower.satellites",
"srcArrow": "none",
"srcLabel": "",
"dst": "network.cell tower.transmitter",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"label": "SEND",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 38,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 241,
"y": 477
},
{
"x": 241.5,
"y": 658
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "network.(cell tower.transmitter -> data processor.storage)[0]",
"src": "network.cell tower.transmitter",
"srcArrow": "none",
"srcLabel": "",
"dst": "network.data processor.storage",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"label": "PHONE LOGS",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 96,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 192.5,
"y": 724
},
{
"x": 193,
"y": 995
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(user -> network.cell tower)[0]",
"src": "user",
"srcArrow": "none",
"srcLabel": "",
"dst": "network.cell tower",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"label": "MAKE CALL",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 86,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 358,
"y": 99
},
{
"x": 357.58333333333337,
"y": 165
},
{
"x": 236.83333333333334,
"y": 165
},
{
"x": 236.83333333333334,
"y": 361
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(user -> network.online portal.ui)[0]",
"src": "user",
"srcArrow": "none",
"srcLabel": "",
"dst": "network.online portal.ui",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 3,
"strokeWidth": 2,
"stroke": "B2",
"label": "ACCESS",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 58,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 401,
"y": 99
},
{
"x": 400.9166666666667,
"y": 165
},
{
"x": 444.6666666666667,
"y": 165
},
{
"x": 445,
"y": 416
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(api server -> network.online portal.ui)[0]",
"src": "api server",
"srcArrow": "none",
"srcLabel": "",
"dst": "network.online portal.ui",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"label": "DISPLAY",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 69,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 627.5,
"y": 125
},
{
"x": 627.5,
"y": 266
},
{
"x": 471.33333333333337,
"y": 266
},
{
"x": 471,
"y": 416
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(api server -> logs)[0]",
"src": "api server",
"srcArrow": "none",
"srcLabel": "",
"dst": "logs",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"label": "PERSIST",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 68,
"labelHeight": 21,
"labelPosition": "INSIDE_MIDDLE_CENTER",
"labelPercentage": 0,
"route": [
{
"x": 698.5,
"y": 125
},
{
"x": 698.5,
"y": 165
},
{
"x": 744,
"y": 165
},
{
"x": 744,
"y": 311
}
],
"animated": false,
"tooltip": "",
"icon": null,
"zIndex": 0
},
{
"id": "(network.data processor -> api server)[0]",
"src": "network.data processor",
"srcArrow": "none",
"srcLabel": "",
"dst": "api server",
"dstArrow": "triangle",
"dstLabel": "",
"opacity": 1,
"strokeDash": 0,
"strokeWidth": 2,
"stroke": "B1",
"label": "",
"fontSize": 16,
"fontFamily": "mono",
"language": "",
"color": "N2",
"italic": true,
"bold": false,
"underline": false,
"labelWidth": 0,
"labelHeight": 0,
"labelPosition": "",
"labelPercentage": 0,
"route": [
{
"x": 136.5,
"y": 1163
},
{
"x": 136.5,
"y": 1258
},
{
"x": 663,
"y": 1258
},
{
"x": 663,
"y": 125
}
],
"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
}
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 275 KiB

64
e2etests/themes_test.go Normal file
View file

@ -0,0 +1,64 @@
package e2etests
import (
_ "embed"
"testing"
"oss.terrastruct.com/d2/d2themes/d2themescatalog"
)
func testThemes(t *testing.T) {
tcs := []testCase{
{
name: "terminal",
themeID: d2themescatalog.Terminal.ID,
script: `
network: {
cell tower: {
satellites: {
shape: stored_data
style.multiple: true
}
transmitter
satellites -> transmitter: send
satellites -> transmitter: send
satellites -> transmitter: send
}
online portal: {
ui: { shape: hexagon }
}
data processor: {
storage: {
shape: cylinder
style.multiple: true
}
}
cell tower.transmitter -> data processor.storage: phone logs
}
user: {
shape: person
width: 130
}
user -> network.cell tower: make call
user -> network.online portal.ui: access {
style.stroke-dash: 3
}
api server -> network.online portal.ui: display
api server -> logs: persist
logs: { shape: page; style.multiple: true }
network.data processor -> api server
`,
},
}
runa(t, tcs)
}