374 lines
12 KiB
JSON
374 lines
12 KiB
JSON
{
|
|
"name": "lucid",
|
|
"note": "Visual language for generated diagrams. Keyed on `kind`, which is the only IR field style may read. Rules name SLOTS, never hexes; a theme binds slot -> colour. That indirection is the point: it is the same vocabulary `soleprint/common/theme/tokens.css` and `docs/graphs/themes/*.gvpr` already use, so a diagram and the page around it are one visual language. docs/graphs/README.md states the rule.",
|
|
"default_theme": "dark",
|
|
"themes": {
|
|
"lucid": {
|
|
"note": "Print and light, for documents and anything that gets exported. Extracted from real architecture diagrams, then re-bound onto spr's slots. Ask for it with `--theme lucid`.",
|
|
"slots": {
|
|
"surface-0": "#FFFFFF",
|
|
"surface-1": "#FAFAFA",
|
|
"surface-2": "#F5F7FA",
|
|
"border": "#BDBDBD",
|
|
"border-strong": "#9E9E9E",
|
|
"text": "#333333",
|
|
"text-muted": "#616E7C",
|
|
"text-dim": "#9AA5B1",
|
|
"text-on-fill": "#000000",
|
|
"accent": "#FFB74D",
|
|
"accent-soft": "#FFF3E0",
|
|
"artery": "#E53935",
|
|
"artery-soft": "#FFEBEE",
|
|
"atlas": "#43A047",
|
|
"atlas-soft": "#E8F5E9",
|
|
"station": "#1E88E5",
|
|
"station-soft": "#E3F2FD",
|
|
"ok": "#43A047",
|
|
"error": "#E53935",
|
|
"muted": "#E0E0E0",
|
|
"muted-soft": "#FAFAFA",
|
|
"alt-1": "#B39DDB",
|
|
"alt-1-soft": "#EDE7F6",
|
|
"alt-2": "#FFF176",
|
|
"alt-2-soft": "#FFFDE7"
|
|
}
|
|
},
|
|
"dark": {
|
|
"note": "The default. Every value is a tokens.css variable or a --system-accent, so a diagram embedded in a page matches it without anyone choosing. artery/atlas/station are exactly what artery/index.html:30, atlas/index.html:25 and station/index.html:29 set. `--theme lucid` switches to the print palette.",
|
|
"slots": {
|
|
"surface-0": "#0a0a0a",
|
|
"surface-1": "#141414",
|
|
"surface-2": "#1a1a1a",
|
|
"border": "#333333",
|
|
"border-strong": "#4a4a4a",
|
|
"text": "#e5e5e5",
|
|
"text-muted": "#a3a3a3",
|
|
"text-dim": "#666666",
|
|
"text-on-fill": "#0a0a0a",
|
|
"accent": "#d4a574",
|
|
"accent-soft": "#242424",
|
|
"artery": "#b91c1c",
|
|
"artery-soft": "#fca5a5",
|
|
"atlas": "#15803d",
|
|
"atlas-soft": "#86efac",
|
|
"station": "#1d4ed8",
|
|
"station-soft": "#93c5fd",
|
|
"ok": "#3ecf8e",
|
|
"error": "#f06565",
|
|
"muted": "#555568",
|
|
"muted-soft": "#141414",
|
|
"alt-1": "#4f9cf9",
|
|
"alt-1-soft": "#141414",
|
|
"alt-2": "#f5a623",
|
|
"alt-2-soft": "#141414"
|
|
}
|
|
}
|
|
},
|
|
"geometry": {
|
|
"note": "Pinned to tokens.css rather than retyped. The supplied spec and spr's tokens agreed on six of these independently: radius 4-6px, label 10-11px, header 12-14px, padding 8px, hairline 1px, and a Segoe-UI-through-Arial sans stack.",
|
|
"radius-sm": 4,
|
|
"radius": 6,
|
|
"hairline": 1,
|
|
"font": "Helvetica",
|
|
"font-note": "PostScript-style names, because they are the only spelling Graphviz maps to a real SVG font-weight. `fontname=\"Arial Bold\"` passes through verbatim as font-family=\"Arial Bold\" \u2014 a family no browser has, so it renders neither Arial nor bold. `Helvetica-Bold` emits font-family=\"Helvetica,sans-Serif\" font-weight=\"bold\", which is a real stack with a real weight. It also measures wider (128pt vs 116pt for the same label), so boxes are sized for the text that actually renders instead of overflowing it.",
|
|
"font-size-sm": 10,
|
|
"font-size-base": 11,
|
|
"font-size-header": 13,
|
|
"padding": "0.20,0.10",
|
|
"font-bold": "Helvetica-Bold"
|
|
},
|
|
"graph": {
|
|
"bgcolor": "surface-0",
|
|
"fontcolor": "text",
|
|
"rankdir": "TB",
|
|
"nodesep": 0.5,
|
|
"ranksep": 0.6,
|
|
"pad": 0.3
|
|
},
|
|
"nodes": {
|
|
"default": {
|
|
"shape": "box",
|
|
"rounded": true,
|
|
"fill": "surface-0",
|
|
"border": "border",
|
|
"text": "text",
|
|
"note": "Every unknown kind lands here rather than crashing. A new extractor renders plainly and legibly on day one.",
|
|
"bold": true
|
|
},
|
|
"component": {
|
|
"shape": "box",
|
|
"rounded": true,
|
|
"fill": "surface-0",
|
|
"border": "border",
|
|
"text": "text",
|
|
"bold": true
|
|
},
|
|
"datastore": {
|
|
"shape": "cylinder",
|
|
"rounded": false,
|
|
"fill": "surface-0",
|
|
"border": "border",
|
|
"text": "text",
|
|
"bold": true
|
|
},
|
|
"bucket": {
|
|
"shape": "trapezium",
|
|
"rounded": false,
|
|
"fill": "surface-0",
|
|
"border": "border",
|
|
"text": "text",
|
|
"note": "An approximation. DOT has no S3-bucket silhouette; trapezium is the nearest native shape.",
|
|
"bold": true
|
|
},
|
|
"module": {
|
|
"shape": "box",
|
|
"rounded": true,
|
|
"fill": "surface-2",
|
|
"border": "border-strong",
|
|
"text": "text",
|
|
"bold": true
|
|
},
|
|
"class": {
|
|
"shape": "box",
|
|
"rounded": true,
|
|
"fill": "surface-0",
|
|
"border": "station",
|
|
"text": "text",
|
|
"bold": true
|
|
},
|
|
"function": {
|
|
"shape": "box",
|
|
"rounded": true,
|
|
"fill": "surface-0",
|
|
"border": "border",
|
|
"text": "text-muted",
|
|
"font-size": "font-size-sm",
|
|
"bold": true
|
|
},
|
|
"external": {
|
|
"shape": "box",
|
|
"rounded": true,
|
|
"fill": "muted-soft",
|
|
"border": "muted",
|
|
"text": "text-muted",
|
|
"dashed": true,
|
|
"note": "A name that could not be resolved. Drawn as the boundary it is, and never dropped \u2014 a missing dependency that looks like a complete diagram is the worse failure.",
|
|
"bold": true
|
|
},
|
|
"task": {
|
|
"shape": "box",
|
|
"rounded": true,
|
|
"fill": "surface-2",
|
|
"border": "station",
|
|
"text": "text",
|
|
"bold": true,
|
|
"note": "A unit of work in a pipeline \u2014 an Airflow task, a build step. Distinct from `module` so a schedule does not render as if it were code structure."
|
|
},
|
|
"sensor": {
|
|
"shape": "hexagon",
|
|
"rounded": false,
|
|
"fill": "surface-2",
|
|
"border": "accent",
|
|
"text": "text",
|
|
"bold": true,
|
|
"note": "A task that waits on something outside the pipeline."
|
|
},
|
|
"endpoint": {
|
|
"shape": "box",
|
|
"rounded": true,
|
|
"fill": "surface-2",
|
|
"border": "station",
|
|
"text": "text",
|
|
"bold": true,
|
|
"note": "An HTTP route. From a spec, or from what was actually called."
|
|
},
|
|
"operation": {
|
|
"shape": "box",
|
|
"rounded": true,
|
|
"fill": "surface-2",
|
|
"border": "accent",
|
|
"text": "text",
|
|
"bold": true,
|
|
"note": "A GraphQL operation \u2014 usually one endpoint carrying many, which is why it is its own kind rather than a path."
|
|
}
|
|
},
|
|
"groups": {
|
|
"default": {
|
|
"rounded": false,
|
|
"dashed": true,
|
|
"fill": "surface-2",
|
|
"border": "border",
|
|
"text": "text-muted",
|
|
"bold": true
|
|
},
|
|
"boundary": {
|
|
"rounded": false,
|
|
"dashed": false,
|
|
"fill": "surface-0",
|
|
"border": "border-strong",
|
|
"header-fill": "accent",
|
|
"text": "text-on-fill",
|
|
"bold": true,
|
|
"note": "Type A. The header is a filled bar across the top of the container in the source spec; DOT gives a label plus a cluster bgcolor and no separate bar. See limits.header-bar."
|
|
},
|
|
"zone": {
|
|
"rounded": false,
|
|
"dashed": true,
|
|
"fill": "surface-2",
|
|
"border": "border",
|
|
"text": "text-muted",
|
|
"note": "Type B. Dashed area boundary.",
|
|
"bold": true
|
|
},
|
|
"artery": {
|
|
"rounded": false,
|
|
"dashed": true,
|
|
"fill": "artery-soft",
|
|
"border": "artery",
|
|
"text": "text-muted",
|
|
"bold": true
|
|
},
|
|
"atlas": {
|
|
"rounded": false,
|
|
"dashed": true,
|
|
"fill": "atlas-soft",
|
|
"border": "atlas",
|
|
"text": "text-muted",
|
|
"bold": true
|
|
},
|
|
"station": {
|
|
"rounded": false,
|
|
"dashed": true,
|
|
"fill": "station-soft",
|
|
"border": "station",
|
|
"text": "text-muted",
|
|
"bold": true
|
|
},
|
|
"pipeline": {
|
|
"rounded": false,
|
|
"dashed": true,
|
|
"fill": "surface-2",
|
|
"border": "border",
|
|
"text": "text-muted",
|
|
"bold": true,
|
|
"note": "One DAG, when several are drawn together."
|
|
}
|
|
},
|
|
"edges": {
|
|
"default": {
|
|
"color": "text",
|
|
"arrowhead": "normal",
|
|
"arrowsize": 0.7,
|
|
"text": "text-muted"
|
|
},
|
|
"imports": {
|
|
"color": "border-strong",
|
|
"arrowhead": "normal",
|
|
"arrowsize": 0.6,
|
|
"text": "text-dim"
|
|
},
|
|
"inherits": {
|
|
"color": "station",
|
|
"arrowhead": "empty",
|
|
"arrowsize": 0.8,
|
|
"text": "text-muted"
|
|
},
|
|
"calls": {
|
|
"color": "text",
|
|
"arrowhead": "normal",
|
|
"arrowsize": 0.7,
|
|
"text": "text-muted"
|
|
},
|
|
"async": {
|
|
"color": "text",
|
|
"arrowhead": "normal",
|
|
"arrowsize": 0.7,
|
|
"dashed": true,
|
|
"text": "text-muted"
|
|
},
|
|
"flow": {
|
|
"color": "artery",
|
|
"arrowhead": "normal",
|
|
"arrowsize": 0.7,
|
|
"dashed": true,
|
|
"text": "artery"
|
|
},
|
|
"ok": {
|
|
"color": "ok",
|
|
"arrowhead": "normal",
|
|
"arrowsize": 0.7,
|
|
"dashed": true,
|
|
"text": "ok"
|
|
},
|
|
"foreign_key": {
|
|
"color": "station",
|
|
"arrowhead": "normal",
|
|
"arrowsize": 0.7,
|
|
"text": "text-muted"
|
|
},
|
|
"references": {
|
|
"color": "border-strong",
|
|
"arrowhead": "normal",
|
|
"arrowsize": 0.6,
|
|
"dashed": true,
|
|
"text": "text-dim"
|
|
},
|
|
"depends": {
|
|
"color": "border-strong",
|
|
"arrowhead": "normal",
|
|
"arrowsize": 0.7,
|
|
"text": "text-muted"
|
|
},
|
|
"triggers": {
|
|
"color": "accent",
|
|
"arrowhead": "normal",
|
|
"arrowsize": 0.8,
|
|
"text": "accent"
|
|
},
|
|
"follows": {
|
|
"color": "accent",
|
|
"arrowhead": "normal",
|
|
"arrowsize": 0.7,
|
|
"text": "accent",
|
|
"note": "Observed order. Consecutive, not caused-by; the weight is the signal."
|
|
},
|
|
"accepts": {
|
|
"color": "border-strong",
|
|
"arrowhead": "normal",
|
|
"arrowsize": 0.6,
|
|
"text": "text-dim"
|
|
},
|
|
"returns": {
|
|
"color": "station",
|
|
"arrowhead": "normal",
|
|
"arrowsize": 0.6,
|
|
"text": "text-dim"
|
|
}
|
|
},
|
|
"domain_slots": {
|
|
"note": "How a group picks its colour without the IR ever naming one. The IR says which spr model a group belongs to (attrs.domain \u2014 semantic); this maps that to a slot. Same mechanism as --system-accent. Where a group has no domain, the emitter assigns the rotation below by sorted id, so the choice is deterministic and two runs give the same bytes.",
|
|
"artery": "artery",
|
|
"atlas": "atlas",
|
|
"station": "station",
|
|
"soleprint": "accent",
|
|
"rotation": [
|
|
"accent",
|
|
"alt-1",
|
|
"alt-2",
|
|
"station",
|
|
"atlas",
|
|
"artery"
|
|
]
|
|
},
|
|
"limits": {
|
|
"note": "Where DOT stops. Recorded rather than worked around; the full spec is kept here so a richer emitter needs no re-authoring. See the plan's 'Use DOT until it hits its limits'.",
|
|
"header-bar": "Type A's filled 100%-width header rectangle. DOT clusters have a label and a bgcolor, not a header bar. Needs an HTML-like label table.",
|
|
"dasharray": "stroke-dasharray is not parameterised \u2014 style=dashed is one pattern, so 4,4 and 5,5 collapse. Async and thematic edges must differ by colour, not by dash.",
|
|
"corner-radius": "style=rounded is binary, so radius-sm (4) and radius (6) render identically.",
|
|
"icon-above-label": "Icon centered above a centered label needs an HTML-like label or image+labelloc; text only here.",
|
|
"sequence-badge": "A numbered circle on an edge. xlabel carries the number; the circle, its fill and its border are not expressible.",
|
|
"bucket-shape": "trapezium approximates the S3 bucket silhouette.",
|
|
"ranged-widths": "The spec gives 1-2px and 1-1.5px; penwidth is scalar, so these pin to geometry.hairline."
|
|
}
|
|
}
|