@coldtea/pr-lens-renderer 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +91 -0
  3. package/dist/bounds.d.ts +23 -0
  4. package/dist/bounds.d.ts.map +1 -0
  5. package/dist/bounds.js +39 -0
  6. package/dist/bounds.js.map +1 -0
  7. package/dist/corrections.d.ts +12 -0
  8. package/dist/corrections.d.ts.map +1 -0
  9. package/dist/corrections.js +122 -0
  10. package/dist/corrections.js.map +1 -0
  11. package/dist/design.d.ts +112 -0
  12. package/dist/design.d.ts.map +1 -0
  13. package/dist/design.js +112 -0
  14. package/dist/design.js.map +1 -0
  15. package/dist/errors.d.ts +11 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +9 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/geometry.d.ts +20 -0
  20. package/dist/geometry.d.ts.map +1 -0
  21. package/dist/geometry.js +15 -0
  22. package/dist/geometry.js.map +1 -0
  23. package/dist/glob.d.ts +7 -0
  24. package/dist/glob.d.ts.map +1 -0
  25. package/dist/glob.js +17 -0
  26. package/dist/glob.js.map +1 -0
  27. package/dist/index.d.ts +8 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +8 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/layout/architecture.d.ts +78 -0
  32. package/dist/layout/architecture.d.ts.map +1 -0
  33. package/dist/layout/architecture.js +207 -0
  34. package/dist/layout/architecture.js.map +1 -0
  35. package/dist/layout/congestion.d.ts +18 -0
  36. package/dist/layout/congestion.d.ts.map +1 -0
  37. package/dist/layout/congestion.js +50 -0
  38. package/dist/layout/congestion.js.map +1 -0
  39. package/dist/layout/dataflow.d.ts +85 -0
  40. package/dist/layout/dataflow.d.ts.map +1 -0
  41. package/dist/layout/dataflow.js +184 -0
  42. package/dist/layout/dataflow.js.map +1 -0
  43. package/dist/layout/edges.d.ts +48 -0
  44. package/dist/layout/edges.d.ts.map +1 -0
  45. package/dist/layout/edges.js +843 -0
  46. package/dist/layout/edges.js.map +1 -0
  47. package/dist/layout/labels.d.ts +9 -0
  48. package/dist/layout/labels.d.ts.map +1 -0
  49. package/dist/layout/labels.js +149 -0
  50. package/dist/layout/labels.js.map +1 -0
  51. package/dist/layout/rank.d.ts +16 -0
  52. package/dist/layout/rank.d.ts.map +1 -0
  53. package/dist/layout/rank.js +80 -0
  54. package/dist/layout/rank.js.map +1 -0
  55. package/dist/layout/seating.d.ts +29 -0
  56. package/dist/layout/seating.d.ts.map +1 -0
  57. package/dist/layout/seating.js +131 -0
  58. package/dist/layout/seating.js.map +1 -0
  59. package/dist/manifest.d.ts +39 -0
  60. package/dist/manifest.d.ts.map +1 -0
  61. package/dist/manifest.js +99 -0
  62. package/dist/manifest.js.map +1 -0
  63. package/dist/render.d.ts +49 -0
  64. package/dist/render.d.ts.map +1 -0
  65. package/dist/render.js +125 -0
  66. package/dist/render.js.map +1 -0
  67. package/dist/scope.d.ts +21 -0
  68. package/dist/scope.d.ts.map +1 -0
  69. package/dist/scope.js +57 -0
  70. package/dist/scope.js.map +1 -0
  71. package/dist/svg/architecture.d.ts +15 -0
  72. package/dist/svg/architecture.d.ts.map +1 -0
  73. package/dist/svg/architecture.js +197 -0
  74. package/dist/svg/architecture.js.map +1 -0
  75. package/dist/svg/dataflow.d.ts +9 -0
  76. package/dist/svg/dataflow.d.ts.map +1 -0
  77. package/dist/svg/dataflow.js +284 -0
  78. package/dist/svg/dataflow.js.map +1 -0
  79. package/dist/svg/document.d.ts +37 -0
  80. package/dist/svg/document.d.ts.map +1 -0
  81. package/dist/svg/document.js +148 -0
  82. package/dist/svg/document.js.map +1 -0
  83. package/dist/svg/icons.d.ts +3 -0
  84. package/dist/svg/icons.d.ts.map +1 -0
  85. package/dist/svg/icons.js +106 -0
  86. package/dist/svg/icons.js.map +1 -0
  87. package/dist/svg/primitives.d.ts +13 -0
  88. package/dist/svg/primitives.d.ts.map +1 -0
  89. package/dist/svg/primitives.js +34 -0
  90. package/dist/svg/primitives.js.map +1 -0
  91. package/dist/svg/pulse.d.ts +24 -0
  92. package/dist/svg/pulse.d.ts.map +1 -0
  93. package/dist/svg/pulse.js +33 -0
  94. package/dist/svg/pulse.js.map +1 -0
  95. package/dist/text.d.ts +18 -0
  96. package/dist/text.d.ts.map +1 -0
  97. package/dist/text.js +70 -0
  98. package/dist/text.js.map +1 -0
  99. package/dist/theme.d.ts +44 -0
  100. package/dist/theme.d.ts.map +1 -0
  101. package/dist/theme.js +72 -0
  102. package/dist/theme.js.map +1 -0
  103. package/dist/version.d.ts +9 -0
  104. package/dist/version.d.ts.map +1 -0
  105. package/dist/version.js +9 -0
  106. package/dist/version.js.map +1 -0
  107. package/package.json +56 -0
  108. package/src/bounds.ts +50 -0
  109. package/src/corrections.ts +166 -0
  110. package/src/design.ts +124 -0
  111. package/src/errors.ts +21 -0
  112. package/src/geometry.ts +22 -0
  113. package/src/glob.ts +20 -0
  114. package/src/index.ts +30 -0
  115. package/src/layout/architecture.ts +324 -0
  116. package/src/layout/congestion.ts +76 -0
  117. package/src/layout/dataflow.ts +307 -0
  118. package/src/layout/edges.ts +1061 -0
  119. package/src/layout/labels.ts +192 -0
  120. package/src/layout/rank.ts +95 -0
  121. package/src/layout/seating.ts +189 -0
  122. package/src/manifest.ts +118 -0
  123. package/src/render.ts +190 -0
  124. package/src/scope.ts +67 -0
  125. package/src/svg/architecture.ts +293 -0
  126. package/src/svg/dataflow.ts +408 -0
  127. package/src/svg/document.ts +198 -0
  128. package/src/svg/icons.ts +121 -0
  129. package/src/svg/primitives.ts +50 -0
  130. package/src/svg/pulse.ts +48 -0
  131. package/src/text.ts +92 -0
  132. package/src/theme.ts +107 -0
  133. package/src/version.ts +9 -0
@@ -0,0 +1,99 @@
1
+ import { createHash } from "node:crypto";
2
+ import { SCHEMA_VERSION } from "@coldtea/pr-lens-schema";
3
+ import { RENDERER_NAME, RENDERER_VERSION } from "./version.js";
4
+ /**
5
+ * How many hex characters of the digest an address carries. Long enough that
6
+ * two different renders will not collide inside one repository's history,
7
+ * short enough to read in a URL.
8
+ */
9
+ export const CONTENT_HASH_LENGTH = 32;
10
+ /**
11
+ * The one place render bytes become an address.
12
+ *
13
+ * GitHub proxies comment images and caches them hard, so a changed diagram
14
+ * has to arrive as a different URL rather than as new bytes at the old one.
15
+ * Every surface that builds such a URL — the action writing files, the hosted
16
+ * app uploading them — must hash through here, or the two will disagree about
17
+ * what the same render is called.
18
+ */
19
+ export const contentHash = (bytes) => createHash("sha256").update(bytes, "utf8").digest("hex").slice(0, CONTENT_HASH_LENGTH);
20
+ /** JSON with object keys in sorted order, so equal documents hash equal. */
21
+ export const canonicalJson = (value) => {
22
+ if (value === null || typeof value !== "object")
23
+ return JSON.stringify(value) ?? "null";
24
+ if (Array.isArray(value))
25
+ return `[${value.map(canonicalJson).join(",")}]`;
26
+ const entries = Object.entries(value)
27
+ .filter(([, nested]) => nested !== undefined)
28
+ .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
29
+ return `{${entries.map(([key, nested]) => `${JSON.stringify(key)}:${canonicalJson(nested)}`).join(",")}}`;
30
+ };
31
+ export const graphContentHash = (doc) => contentHash(canonicalJson(doc));
32
+ /**
33
+ * How the characters a view id may contain but a file name may not are spelled.
34
+ *
35
+ * View ids are authored by an extraction model, and the contract lets one
36
+ * contain `/`, `:` and `.` — so `a/../../elsewhere` is a perfectly valid id.
37
+ * Surfaces join an asset's path to an output directory, and that id would walk
38
+ * straight out of it. The mapping is one-to-one rather than a scrub, so two
39
+ * different views can never be handed the same address: `_` names itself
40
+ * first, which is what keeps the other three from colliding with text that
41
+ * was already there.
42
+ */
43
+ const FILE_NAME_ESCAPES = [
44
+ ["_", "_u"],
45
+ ["/", "_s"],
46
+ [":", "_c"],
47
+ [".", "_d"],
48
+ ];
49
+ const escapeForFileName = (id) => FILE_NAME_ESCAPES.reduce((text, [character, escape]) => text.split(character).join(escape), id);
50
+ /** The contract's ceiling on an `Id`, which an asset id has to stay under. */
51
+ const ASSET_ID_MAX = 128;
52
+ const THEME_SUFFIX_MAX = "-light".length;
53
+ const STEM_MAX = ASSET_ID_MAX - THEME_SUFFIX_MAX;
54
+ const DIGEST_LENGTH = 12;
55
+ /**
56
+ * `_h` cannot occur in an escaped id — every `_` there opens one of the four
57
+ * escapes above — so it can mark the digest without ever being mistaken for
58
+ * text that was already in the id.
59
+ */
60
+ const DIGEST_MARKER = "_h";
61
+ const PREFIX_MAX = STEM_MAX - DIGEST_MARKER.length - DIGEST_LENGTH;
62
+ /**
63
+ * A view id spelled so that it is both a legal file name and a legal `Id`.
64
+ *
65
+ * Escaping can double the length of an id that is already near the contract's
66
+ * limit, so a stem that would overflow keeps a readable prefix and ends in a
67
+ * digest of the whole original. The digest is what preserves the one property
68
+ * that matters: two different views can never be handed the same address.
69
+ */
70
+ const fileNameSafe = (id) => {
71
+ const escaped = escapeForFileName(id);
72
+ if (escaped.length <= STEM_MAX)
73
+ return escaped;
74
+ const prefix = escaped.slice(0, PREFIX_MAX).replace(/_$/, "");
75
+ return `${prefix}${DIGEST_MARKER}${contentHash(id).slice(0, DIGEST_LENGTH)}`;
76
+ };
77
+ /**
78
+ * The identity of one rendered SVG within a render: the drill-down section it
79
+ * belongs to and the half of the theme pair it is. A render with no sections
80
+ * falls back to the lens, which is unique across the same render.
81
+ *
82
+ * This doubles as the stem of the file name, so it is spelled in the alphabet
83
+ * a file name can carry.
84
+ */
85
+ export const renderAssetId = ({ lens, theme, view }) => `${fileNameSafe(view ?? lens)}-${theme}`;
86
+ /** The file name an asset is written or published under. */
87
+ export const renderAssetFileName = (address, hash) => `${renderAssetId(address)}-${hash}.svg`;
88
+ export const buildManifest = (doc, assets) => ({
89
+ schemaVersion: SCHEMA_VERSION,
90
+ kind: "render-manifest",
91
+ graph: {
92
+ id: doc.id,
93
+ headSha: doc.provenance.head.sha,
94
+ contentHash: graphContentHash(doc),
95
+ },
96
+ renderer: { name: RENDERER_NAME, version: RENDERER_VERSION },
97
+ assets: [...assets],
98
+ });
99
+ //# sourceMappingURL=manifest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { Config, GraphDoc, Lens, RenderAsset, RenderManifest } from "@coldtea/pr-lens-schema";
2
+ import { type Theme } from "./theme.js";
3
+ export type RenderOptions = {
4
+ lens: Lens;
5
+ theme: Theme;
6
+ /** Id of the drill-down section to draw. Omitted renders the whole document. */
7
+ view?: string;
8
+ /** The repository's corrections, applied as an overlay before layout. */
9
+ config?: Config;
10
+ };
11
+ export type RenderedSvg = {
12
+ svg: string;
13
+ width: number;
14
+ height: number;
15
+ lens: Lens;
16
+ theme: Theme;
17
+ view: string | undefined;
18
+ animated: boolean;
19
+ };
20
+ /**
21
+ * A schema-valid document in, one self-contained SVG out.
22
+ *
23
+ * Nothing here reads a clock, a file or a random number: the same document
24
+ * and options produce the same bytes, on any machine, in any order. That is
25
+ * what lets a render be addressed by its own hash, and what keeps a diagram
26
+ * from rearranging itself between two pushes that barely changed anything.
27
+ */
28
+ export declare const render: (doc: GraphDoc, options: RenderOptions) => RenderedSvg;
29
+ export type RenderAllOptions = {
30
+ config?: Config;
31
+ /** Which halves of the theme pair to produce. Both, by default. */
32
+ themes?: readonly Theme[];
33
+ };
34
+ export type RenderedAsset = RenderedSvg & {
35
+ asset: RenderAsset;
36
+ };
37
+ export type RenderAllResult = {
38
+ assets: RenderedAsset[];
39
+ manifest: RenderManifest;
40
+ };
41
+ /**
42
+ * Every drill-down section a comment will show, in both themes.
43
+ *
44
+ * A document with no sections still renders: it gets one diagram per lens it
45
+ * declares, which is what a comment falls back to when extraction found
46
+ * nothing worth splitting into a tree.
47
+ */
48
+ export declare const renderAll: (doc: GraphDoc, options?: RenderAllOptions) => RenderAllResult;
49
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,cAAc,EAAmB,MAAM,yBAAyB,CAAC;AAMpH,OAAO,EAAsB,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAK5D,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,KAAK,CAAC;IACb,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAoCF;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM,QAAS,QAAQ,WAAW,aAAa,KAAG,WA0B9D,CAAC;AASF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mEAAmE;IACnE,MAAM,CAAC,EAAE,SAAS,KAAK,EAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG;IAAE,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC;AAEjE,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,QAAQ,EAAE,cAAc,CAAC;CAC1B,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,QAAS,QAAQ,YAAW,gBAAgB,KAAQ,eA8DzE,CAAC"}
package/dist/render.js ADDED
@@ -0,0 +1,125 @@
1
+ import { assertNever, MAX_RENDER_ASSETS } from "@coldtea/pr-lens-schema";
2
+ import { applyCorrections } from "./corrections.js";
3
+ import { PrLensRenderError } from "./errors.js";
4
+ import { buildManifest, contentHash, renderAssetFileName, renderAssetId } from "./manifest.js";
5
+ import { findView, flattenViews, resolveScope } from "./scope.js";
6
+ import { paletteFor, THEMES } from "./theme.js";
7
+ import { paintArchitecture } from "./svg/architecture.js";
8
+ import { paintDataFlow } from "./svg/dataflow.js";
9
+ import { svgDocument } from "./svg/document.js";
10
+ const WHOLE_DOCUMENT = { kind: "all" };
11
+ const prepare = (doc, config) => config === undefined ? doc : applyCorrections(doc, config.map);
12
+ const paint = (lens, graph, doc, theme) => {
13
+ const palette = paletteFor(theme);
14
+ switch (lens) {
15
+ case "architecture": {
16
+ if (graph.nodes.length === 0)
17
+ throw new PrLensRenderError("NOTHING_TO_RENDER", "no nodes are in scope for this view");
18
+ const painting = paintArchitecture(graph, doc.layout, palette);
19
+ return { ...painting, animated: graph.edges.some((edge) => edge.animated) };
20
+ }
21
+ case "data-flow": {
22
+ if (graph.flows.length === 0)
23
+ throw new PrLensRenderError("NO_FLOW_IN_SCOPE", "the data-flow lens needs a flow to draw");
24
+ const painting = paintDataFlow(graph.flows, doc.nodes, palette);
25
+ return {
26
+ ...painting,
27
+ animated: graph.flows.some((flow) => flow.messages.some((message) => message.animated)),
28
+ };
29
+ }
30
+ default:
31
+ return assertNever(lens, "Unhandled lens");
32
+ }
33
+ };
34
+ /**
35
+ * A schema-valid document in, one self-contained SVG out.
36
+ *
37
+ * Nothing here reads a clock, a file or a random number: the same document
38
+ * and options produce the same bytes, on any machine, in any order. That is
39
+ * what lets a render be addressed by its own hash, and what keeps a diagram
40
+ * from rearranging itself between two pushes that barely changed anything.
41
+ */
42
+ export const render = (doc, options) => {
43
+ const prepared = prepare(doc, options.config);
44
+ if (!prepared.lenses.includes(options.lens))
45
+ throw new PrLensRenderError("LENS_NOT_DECLARED", `this document does not declare the '${options.lens}' lens`);
46
+ const view = options.view === undefined ? undefined : requireView(prepared.views, options.view);
47
+ const scope = view?.scope ?? WHOLE_DOCUMENT;
48
+ const graph = resolveScope(prepared, scope);
49
+ const { width, height, body, animated } = paint(options.lens, graph, prepared, options.theme);
50
+ const svg = svgDocument({
51
+ width,
52
+ height,
53
+ palette: paletteFor(options.theme),
54
+ title: view?.title ?? prepared.title,
55
+ description: view?.summary ?? prepared.summary,
56
+ body,
57
+ });
58
+ return { svg, width, height, lens: options.lens, theme: options.theme, view: view?.id, animated };
59
+ };
60
+ const requireView = (views, id) => {
61
+ const view = findView(views, id);
62
+ if (view === undefined)
63
+ throw new PrLensRenderError("UNKNOWN_VIEW", `this document has no view '${id}'`);
64
+ return view;
65
+ };
66
+ /**
67
+ * Every drill-down section a comment will show, in both themes.
68
+ *
69
+ * A document with no sections still renders: it gets one diagram per lens it
70
+ * declares, which is what a comment falls back to when extraction found
71
+ * nothing worth splitting into a tree.
72
+ */
73
+ export const renderAll = (doc, options = {}) => {
74
+ const prepared = prepare(doc, options.config);
75
+ const themes = options.themes ?? THEMES;
76
+ const targets = prepared.views.length > 0
77
+ ? flattenViews(prepared.views)
78
+ .filter((view) => prepared.lenses.includes(view.lens))
79
+ .map((view) => ({ lens: view.lens, view }))
80
+ : prepared.lenses
81
+ .filter((lens) => lens !== "data-flow" || prepared.flows.length > 0)
82
+ .map((lens) => ({ lens, view: undefined }));
83
+ /**
84
+ * The contract caps a view tree at the number of views a two-theme render
85
+ * fits inside a manifest, so a parsed document cannot reach this. A
86
+ * hand-built one can: the cap lives in a refinement, and a refinement does
87
+ * not survive into the inferred type. This is a postcondition on what is
88
+ * about to be produced rather than a re-reading of what came in — the count
89
+ * depends on how many themes the caller asked for, which no document knows.
90
+ */
91
+ if (targets.length * themes.length > MAX_RENDER_ASSETS)
92
+ throw new PrLensRenderError("TOO_MANY_ASSETS", `this document asks for ${targets.length * themes.length} pictures across ${targets.length} views ` +
93
+ `and ${themes.length} themes, and a render manifest carries at most ${MAX_RENDER_ASSETS}`);
94
+ const assets = [];
95
+ for (const target of targets)
96
+ for (const theme of themes) {
97
+ const rendered = render(prepared, {
98
+ lens: target.lens,
99
+ theme,
100
+ view: target.view?.id,
101
+ });
102
+ const address = { lens: target.lens, theme, view: target.view?.id };
103
+ const hash = contentHash(rendered.svg);
104
+ assets.push({
105
+ ...rendered,
106
+ asset: {
107
+ id: renderAssetId(address),
108
+ lens: target.lens,
109
+ theme,
110
+ view: target.view?.id,
111
+ mediaType: "image/svg+xml",
112
+ contentHash: hash,
113
+ bytes: Buffer.byteLength(rendered.svg, "utf8"),
114
+ width: rendered.width,
115
+ height: rendered.height,
116
+ animated: rendered.animated,
117
+ path: renderAssetFileName(address, hash),
118
+ },
119
+ });
120
+ }
121
+ if (assets.length === 0)
122
+ throw new PrLensRenderError("NOTHING_TO_RENDER", "this document declares no renderable view");
123
+ return { assets, manifest: buildManifest(prepared, assets.map(({ asset }) => asset)) };
124
+ };
125
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { Flow, GraphDoc, GraphEdge, GraphNode, Lane, View, ViewScope } from "@coldtea/pr-lens-schema";
2
+ /** The slice of a document one SVG draws. */
3
+ export type ScopedGraph = {
4
+ lanes: Lane[];
5
+ nodes: GraphNode[];
6
+ edges: GraphEdge[];
7
+ flows: Flow[];
8
+ };
9
+ export declare const findView: (views: readonly View[], id: string) => View | undefined;
10
+ export declare const flattenViews: (views: readonly View[]) => View[];
11
+ /**
12
+ * Turns a view's scope into the elements to draw.
13
+ *
14
+ * A selection pulls in what its members need to make sense: the lanes its
15
+ * nodes sit in, and both ends of every edge it names, since half an arrow is
16
+ * worse than none. Edges are the one place the author keeps full control —
17
+ * name any and exactly those are drawn, name none and the connections between
18
+ * the selected nodes come along.
19
+ */
20
+ export declare const resolveScope: (doc: GraphDoc, scope: ViewScope) => ScopedGraph;
21
+ //# sourceMappingURL=scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.d.ts","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAG3G,6CAA6C;AAC7C,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,IAAI,EAAE,CAAC;CACf,CAAC;AAEF,eAAO,MAAM,QAAQ,UAAW,SAAS,IAAI,EAAE,MAAM,MAAM,KAAG,IAAI,GAAG,SAOpE,CAAC;AAEF,eAAO,MAAM,YAAY,UAAW,SAAS,IAAI,EAAE,KAAG,IAAI,EACO,CAAC;AAElE;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,QAAS,QAAQ,SAAS,SAAS,KAAG,WAkC9D,CAAC"}
package/dist/scope.js ADDED
@@ -0,0 +1,57 @@
1
+ import { assertNever } from "@coldtea/pr-lens-schema";
2
+ export const findView = (views, id) => {
3
+ for (const view of views) {
4
+ if (view.id === id)
5
+ return view;
6
+ const nested = findView(view.children, id);
7
+ if (nested)
8
+ return nested;
9
+ }
10
+ return undefined;
11
+ };
12
+ export const flattenViews = (views) => views.flatMap((view) => [view, ...flattenViews(view.children)]);
13
+ /**
14
+ * Turns a view's scope into the elements to draw.
15
+ *
16
+ * A selection pulls in what its members need to make sense: the lanes its
17
+ * nodes sit in, and both ends of every edge it names, since half an arrow is
18
+ * worse than none. Edges are the one place the author keeps full control —
19
+ * name any and exactly those are drawn, name none and the connections between
20
+ * the selected nodes come along.
21
+ */
22
+ export const resolveScope = (doc, scope) => {
23
+ switch (scope.kind) {
24
+ case "all":
25
+ return { lanes: [...doc.lanes], nodes: [...doc.nodes], edges: [...doc.edges], flows: [...doc.flows] };
26
+ case "selection": {
27
+ const selectedLanes = new Set(scope.lanes);
28
+ const selectedEdges = new Set(scope.edges);
29
+ const selectedFlows = new Set(scope.flows);
30
+ const flows = doc.flows.filter((flow) => selectedFlows.has(flow.id));
31
+ const nodeIds = new Set(scope.nodes);
32
+ for (const node of doc.nodes)
33
+ if (selectedLanes.has(node.lane))
34
+ nodeIds.add(node.id);
35
+ for (const edge of doc.edges) {
36
+ if (!selectedEdges.has(edge.id))
37
+ continue;
38
+ nodeIds.add(edge.from);
39
+ nodeIds.add(edge.to);
40
+ }
41
+ for (const flow of flows)
42
+ for (const participant of flow.participants)
43
+ nodeIds.add(participant.node);
44
+ const nodes = doc.nodes.filter((node) => nodeIds.has(node.id));
45
+ const edges = scope.edges.length > 0
46
+ ? doc.edges.filter((edge) => selectedEdges.has(edge.id))
47
+ : doc.edges.filter((edge) => nodeIds.has(edge.from) && nodeIds.has(edge.to));
48
+ const laneIds = new Set(scope.lanes);
49
+ for (const node of nodes)
50
+ laneIds.add(node.lane);
51
+ return { lanes: doc.lanes.filter((lane) => laneIds.has(lane.id)), nodes, edges, flows };
52
+ }
53
+ default:
54
+ return assertNever(scope, "Unhandled view scope");
55
+ }
56
+ };
57
+ //# sourceMappingURL=scope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scope.js","sourceRoot":"","sources":["../src/scope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { LayoutHints } from "@coldtea/pr-lens-schema";
2
+ import { type Box } from "../geometry.js";
3
+ import { type PlacedNode } from "../layout/architecture.js";
4
+ import type { ScopedGraph } from "../scope.js";
5
+ import type { Palette } from "../theme.js";
6
+ import { type Tone } from "./document.js";
7
+ export declare const paintCard: (placed: PlacedNode) => string;
8
+ export declare const paintLabelPill: (text: string, box: Box, tone: Tone) => string;
9
+ export type ArchitecturePainting = {
10
+ width: number;
11
+ height: number;
12
+ body: string;
13
+ };
14
+ export declare const paintArchitecture: (graph: ScopedGraph, hints: LayoutHints | undefined, palette: Palette) => ArchitecturePainting;
15
+ //# sourceMappingURL=architecture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"architecture.d.ts","sourceRoot":"","sources":["../../src/svg/architecture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAwB,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAoBjF,OAAO,EAAS,KAAK,GAAG,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAML,KAAK,UAAU,EAChB,MAAM,2BAA2B,CAAC;AAInC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAA2C,KAAK,IAAI,EAAE,MAAM,eAAe,CAAC;AAwDnF,eAAO,MAAM,SAAS,WAAY,UAAU,KAAG,MA8D9C,CAAC;AAqDF,eAAO,MAAM,cAAc,SAAU,MAAM,OAAO,GAAG,QAAQ,IAAI,KAAG,MAqBjE,CAAC;AAEJ,MAAM,MAAM,oBAAoB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnF,eAAO,MAAM,iBAAiB,UACrB,WAAW,SACX,WAAW,GAAG,SAAS,WACrB,OAAO,KACf,oBAyDF,CAAC"}
@@ -0,0 +1,197 @@
1
+ import { assertNever } from "@coldtea/pr-lens-schema";
2
+ import { BADGE_GAP, BADGE_HEIGHT, BADGE_RADIUS, BADGE_RISE, CARD_PADDING_X, CARD_RADIUS, HERO_PULSE_COUNT, ICON_CHIP_GAP, ICON_CHIP_RADIUS, ICON_CHIP_SIZE, LANE_HEADER_BASELINE, LANE_PADDING_X, LANE_RADIUS, SUBTITLE_SIZE, } from "../design.js";
3
+ import { canvasFor, union } from "../bounds.js";
4
+ import { DIAGRAM_MARGIN } from "../design.js";
5
+ import { coord } from "../geometry.js";
6
+ import { badgeRow, badgeWidth, cardBadges, deltaBadgeText, laneHeaderText, } from "../layout/architecture.js";
7
+ import { relieveCongestion } from "../layout/congestion.js";
8
+ import { curveBounds } from "../layout/edges.js";
9
+ import { placeLabelPills } from "../layout/labels.js";
10
+ import { truncate } from "../text.js";
11
+ import { markerFor, shifted, toneColour, toneFor } from "./document.js";
12
+ import { glyphGroup } from "./icons.js";
13
+ import { lines, tag, textNode, wrap } from "./primitives.js";
14
+ import { travellingPulses } from "./pulse.js";
15
+ const badgeTone = (node, text) => text === deltaBadgeText(node.delta) ? toneFor(node.delta) : "neutral";
16
+ /** The badge row, laid left to right across the strip the layout reserved. */
17
+ const paintBadges = (placed) => {
18
+ const row = badgeRow(placed);
19
+ if (row === undefined)
20
+ return "";
21
+ let x = row.box.x;
22
+ return lines(row.badges.map((text) => {
23
+ const width = badgeWidth(text);
24
+ const painted = wrap("g", { class: `bdg bdg-${badgeTone(placed.node, text)}` }, tag("rect", {
25
+ x: coord(x),
26
+ y: coord(row.box.y),
27
+ width: coord(width),
28
+ height: BADGE_HEIGHT,
29
+ rx: BADGE_RADIUS,
30
+ }) +
31
+ textNode({
32
+ x: coord(x + width / 2),
33
+ y: coord(row.box.y + BADGE_HEIGHT / 2 + 3),
34
+ "text-anchor": "middle",
35
+ }, text));
36
+ x += width + BADGE_GAP;
37
+ return painted;
38
+ }));
39
+ };
40
+ const cardOutlineClass = (node) => {
41
+ switch (node.delta) {
42
+ case "added":
43
+ return "card card-added";
44
+ case "modified":
45
+ return "card card-modified";
46
+ case "removed":
47
+ case "unchanged":
48
+ return "card";
49
+ default:
50
+ return assertNever(node.delta, "Unhandled delta");
51
+ }
52
+ };
53
+ export const paintCard = (placed) => {
54
+ const { node, box, showIcon, titleSize } = placed;
55
+ const textX = box.x + CARD_PADDING_X + (showIcon ? ICON_CHIP_SIZE + ICON_CHIP_GAP : 0);
56
+ const textWidth = box.x + box.width - CARD_PADDING_X - textX;
57
+ const hasSubtitle = node.subtitle !== undefined;
58
+ const titleBaseline = box.y + (hasSubtitle ? 27 : 31);
59
+ const chip = showIcon
60
+ ? tag("rect", {
61
+ class: "chip",
62
+ x: coord(box.x + CARD_PADDING_X),
63
+ y: coord(box.y + (box.height - ICON_CHIP_SIZE) / 2),
64
+ width: ICON_CHIP_SIZE,
65
+ height: ICON_CHIP_SIZE,
66
+ rx: ICON_CHIP_RADIUS,
67
+ }) +
68
+ glyphGroup(node.kind, box.x + CARD_PADDING_X + ICON_CHIP_SIZE / 2, box.y + box.height / 2)
69
+ : "";
70
+ const title = textNode({
71
+ class: node.delta === "removed" ? "ntitle strike" : "ntitle",
72
+ x: coord(textX),
73
+ y: coord(titleBaseline),
74
+ "font-size": titleSize,
75
+ }, truncate(node.label, "sans-bold", titleSize, textWidth));
76
+ const subtitle = node.subtitle === undefined
77
+ ? ""
78
+ : textNode({ class: "nsub", x: coord(textX), y: coord(box.y + 45) }, truncate(node.subtitle, "mono", SUBTITLE_SIZE, textWidth));
79
+ const groupClass = node.delta === "removed" ? "cardsh ghost" : node.delta === "unchanged" ? "cardsh context" : "cardsh";
80
+ return wrap("g", { class: groupClass }, lines([
81
+ tag("rect", {
82
+ class: cardOutlineClass(node),
83
+ x: coord(box.x),
84
+ y: coord(box.y),
85
+ width: coord(box.width),
86
+ height: coord(box.height),
87
+ rx: CARD_RADIUS,
88
+ }),
89
+ chip,
90
+ title,
91
+ subtitle,
92
+ paintBadges(placed),
93
+ ]));
94
+ };
95
+ const pulses = (edge, path, palette) => edge.animated
96
+ ? travellingPulses({
97
+ path,
98
+ colour: toneColour(palette, toneFor(edge.delta)),
99
+ count: edge.emphasis === "hero" ? HERO_PULSE_COUNT : 1,
100
+ lag: 0,
101
+ })
102
+ : "";
103
+ /**
104
+ * Paints one edge and the pill settled onto it. Every edge carries at most
105
+ * one label, and the layout already chose its place — on the route's longest
106
+ * straight run, nudged clear of every other pill — so a reader traces a line
107
+ * to its own words.
108
+ *
109
+ * The line and the label come back separately because they belong to
110
+ * different layers: a line passes behind a card, and a pill — which is opaque
111
+ * precisely so that it can be read wherever it lands — passes in front of one.
112
+ */
113
+ const paintEdge = (routed, palette, label) => {
114
+ const { edge, path } = routed;
115
+ const tone = toneFor(edge.delta);
116
+ const hero = edge.emphasis === "hero";
117
+ const classes = ["edge", `edge-${tone}`];
118
+ if (hero)
119
+ classes.push("hero");
120
+ if (edge.emphasis === "muted")
121
+ classes.push("faded");
122
+ if (edge.delta === "unchanged")
123
+ classes.push("context");
124
+ const glow = hero
125
+ ? tag("path", { class: "glow", stroke: toneColour(palette, tone), d: path })
126
+ : "";
127
+ return {
128
+ markup: lines([
129
+ glow,
130
+ tag("path", { class: classes.join(" "), d: path, "marker-end": markerFor(tone) }),
131
+ pulses(edge, path, palette),
132
+ ]),
133
+ pill: label === undefined || edge.label === undefined
134
+ ? ""
135
+ : paintLabelPill(edge.label, label, tone),
136
+ };
137
+ };
138
+ export const paintLabelPill = (text, box, tone) => wrap("g", {}, tag("rect", {
139
+ class: "lpill",
140
+ x: coord(box.x),
141
+ y: coord(box.y),
142
+ width: coord(box.width),
143
+ height: coord(box.height),
144
+ rx: box.height / 2,
145
+ }) +
146
+ textNode({
147
+ class: tone === "neutral" ? "ltext" : `ltext ltext-${tone}`,
148
+ x: coord(box.x + box.width / 2),
149
+ y: coord(box.y + box.height / 2 + 3.5),
150
+ "text-anchor": "middle",
151
+ }, text));
152
+ export const paintArchitecture = (graph, hints, palette) => {
153
+ const { layout, routed } = relieveCongestion(graph, hints);
154
+ const pills = placeLabelPills(routed);
155
+ const drawn = layout.nodes.flatMap((node) => {
156
+ const row = badgeRow(node);
157
+ return row === undefined ? [node.box] : [node.box, row.box];
158
+ });
159
+ const edgeMarkup = [];
160
+ const pillMarkup = [];
161
+ for (const edge of routed) {
162
+ const label = pills.get(edge.edge.id);
163
+ const { markup, pill } = paintEdge(edge, palette, label);
164
+ edgeMarkup.push(markup);
165
+ pillMarkup.push(pill);
166
+ if (label !== undefined)
167
+ drawn.push(label);
168
+ }
169
+ const lanes = layout.lanes.map(({ lane, box }) => lines([
170
+ tag("rect", {
171
+ class: "lanebox",
172
+ x: coord(box.x),
173
+ y: coord(box.y),
174
+ width: coord(box.width),
175
+ height: coord(box.height),
176
+ rx: LANE_RADIUS,
177
+ }),
178
+ textNode({ class: "lanelabel", x: coord(box.x + LANE_PADDING_X), y: LANE_HEADER_BASELINE }, laneHeaderText(lane)),
179
+ ]));
180
+ const canvas = canvasFor(layout, union([
181
+ ...layout.lanes.map(({ box }) => box),
182
+ ...drawn,
183
+ ...routed.map(({ curve }) => curveBounds(curve)),
184
+ ]), DIAGRAM_MARGIN);
185
+ const painted = lines([
186
+ wrap("g", {}, lines(lanes)),
187
+ wrap("g", {}, lines(edgeMarkup)),
188
+ wrap("g", {}, lines(layout.nodes.map(paintCard))),
189
+ wrap("g", {}, lines(pillMarkup)),
190
+ ]);
191
+ return {
192
+ width: canvas.width,
193
+ height: canvas.height,
194
+ body: shifted(canvas, painted),
195
+ };
196
+ };
197
+ //# sourceMappingURL=architecture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"architecture.js","sourceRoot":"","sources":["../../src/svg/architecture.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { Flow, GraphNode } from "@coldtea/pr-lens-schema";
2
+ import type { Palette } from "../theme.js";
3
+ export type DataFlowPainting = {
4
+ width: number;
5
+ height: number;
6
+ body: string;
7
+ };
8
+ export declare const paintDataFlow: (flows: readonly Flow[], nodes: readonly GraphNode[], palette: Palette) => DataFlowPainting;
9
+ //# sourceMappingURL=dataflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataflow.d.ts","sourceRoot":"","sources":["../../src/svg/dataflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAe,SAAS,EAAe,MAAM,yBAAyB,CAAC;AAgCzF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AA4V3C,MAAM,MAAM,gBAAgB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/E,eAAO,MAAM,aAAa,UACjB,SAAS,IAAI,EAAE,SACf,SAAS,SAAS,EAAE,WAClB,OAAO,KACf,gBAqBF,CAAC"}