@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,121 @@
1
+ import type { NodeKind } from "@coldtea/pr-lens-schema";
2
+ import { assertNever } from "@coldtea/pr-lens-schema";
3
+ import { coord } from "../geometry.js";
4
+ import { MONO_STACK } from "../text.js";
5
+ import { lines, tag, textNode, wrap } from "./primitives.js";
6
+
7
+ const stroked = (path: string): string => tag("path", { class: "glyph-stroke", d: path });
8
+
9
+ const glyphText = (cx: number, cy: number, content: string, style: string): string =>
10
+ textNode(
11
+ {
12
+ class: "glyph",
13
+ x: coord(cx),
14
+ y: coord(cy + 4.5),
15
+ "text-anchor": "middle",
16
+ style,
17
+ },
18
+ content,
19
+ );
20
+
21
+ const box = (cx: number, cy: number, width: number, height: number, radius?: number): string =>
22
+ tag("rect", {
23
+ class: "glyph-stroke",
24
+ x: coord(cx - width / 2),
25
+ y: coord(cy - height / 2),
26
+ width: coord(width),
27
+ height: coord(height),
28
+ rx: radius === undefined ? undefined : coord(radius),
29
+ });
30
+
31
+ /**
32
+ * The chip glyph for a node's kind, centred on a point.
33
+ *
34
+ * These say "what sort of thing is this" at a glance and nothing more, which
35
+ * is why the kind enum is coarse. Every kind draws something: a card with an
36
+ * empty chip reads as a rendering fault rather than as an unknown kind.
37
+ */
38
+ const kindGlyph = (kind: NodeKind, cx: number, cy: number): string => {
39
+ switch (kind) {
40
+ case "service":
41
+ return lines([
42
+ box(cx, cy, 13, 13, 3),
43
+ tag("circle", { class: "glyph", cx: coord(cx), cy: coord(cy), r: 2 }),
44
+ ]);
45
+ case "app":
46
+ return lines([
47
+ box(cx, cy, 14, 12, 2),
48
+ stroked(`M${coord(cx - 7)},${coord(cy - 2)} h14`),
49
+ ]);
50
+ case "module":
51
+ return glyphText(cx, cy, "{ }", `font-size:11px;font-family:${MONO_STACK}`);
52
+ case "function":
53
+ return glyphText(cx, cy, "ƒ", "font-size:14px;font-style:italic;font-family:Georgia,serif");
54
+ case "route":
55
+ return tag("path", {
56
+ class: "glyph",
57
+ d: `M${coord(cx - 7)},${coord(cy - 6)} L${coord(cx + 7)},${coord(cy)} L${coord(cx - 7)},${coord(cy + 6)} Z`,
58
+ });
59
+ case "job":
60
+ return lines([
61
+ tag("circle", { class: "glyph-stroke", cx: coord(cx), cy: coord(cy), r: 6.5 }),
62
+ stroked(`M${coord(cx)},${coord(cy - 3.5)} V${coord(cy)} H${coord(cx + 3)}`),
63
+ ]);
64
+ case "queue":
65
+ return lines([
66
+ stroked(`M${coord(cx - 6.5)},${coord(cy - 4.5)} h13`),
67
+ stroked(`M${coord(cx - 6.5)},${coord(cy)} h13`),
68
+ stroked(`M${coord(cx - 6.5)},${coord(cy + 4.5)} h13`),
69
+ ]);
70
+ case "datastore":
71
+ return lines([
72
+ tag("ellipse", { class: "glyph-stroke", cx: coord(cx), cy: coord(cy - 4.5), rx: 6.5, ry: 2.6 }),
73
+ stroked(
74
+ `M${coord(cx - 6.5)},${coord(cy - 4.5)} v9 a6.5,2.6 0 0 0 13 0 v-9`,
75
+ ),
76
+ ]);
77
+ case "cache":
78
+ return tag("path", {
79
+ class: "glyph",
80
+ d: `M${coord(cx + 1)},${coord(cy - 7)} L${coord(cx - 6)},${coord(cy + 1)} H${coord(cx - 1)} L${coord(cx - 1)},${coord(cy + 7)} L${coord(cx + 6)},${coord(cy - 1)} H${coord(cx + 1)} Z`,
81
+ });
82
+ case "external":
83
+ return lines([
84
+ box(cx, cy, 16, 12, 2),
85
+ stroked(`M${coord(cx - 8)},${coord(cy - 4)} l8,6 8,-6`),
86
+ ]);
87
+ case "ui":
88
+ return lines([
89
+ box(cx, cy, 14, 12, 2),
90
+ stroked(`M${coord(cx - 2.5)},${coord(cy - 6)} v12`),
91
+ ]);
92
+ case "config":
93
+ return lines([
94
+ stroked(`M${coord(cx - 7)},${coord(cy - 4)} h14`),
95
+ stroked(`M${coord(cx - 7)},${coord(cy + 4)} h14`),
96
+ tag("circle", { class: "glyph", cx: coord(cx - 2), cy: coord(cy - 4), r: 2.2 }),
97
+ tag("circle", { class: "glyph", cx: coord(cx + 3), cy: coord(cy + 4), r: 2.2 }),
98
+ ]);
99
+ case "test":
100
+ return stroked(`M${coord(cx - 6)},${coord(cy)} l4,4 l8,-8`);
101
+ case "package":
102
+ return lines([
103
+ stroked(
104
+ `M${coord(cx)},${coord(cy - 7)} l6.5,3.5 v7 l-6.5,3.5 l-6.5,-3.5 v-7 Z`,
105
+ ),
106
+ stroked(`M${coord(cx - 6.5)},${coord(cy - 3.5)} l6.5,3.5 l6.5,-3.5`),
107
+ stroked(`M${coord(cx)},${coord(cy)} v7`),
108
+ ]);
109
+ case "other":
110
+ return lines([
111
+ tag("circle", { class: "glyph", cx: coord(cx - 5), cy: coord(cy), r: 1.8 }),
112
+ tag("circle", { class: "glyph", cx: coord(cx), cy: coord(cy), r: 1.8 }),
113
+ tag("circle", { class: "glyph", cx: coord(cx + 5), cy: coord(cy), r: 1.8 }),
114
+ ]);
115
+ default:
116
+ return assertNever(kind, "Unhandled node kind");
117
+ }
118
+ };
119
+
120
+ export const glyphGroup = (kind: NodeKind, cx: number, cy: number): string =>
121
+ wrap("g", {}, kindGlyph(kind, cx, cy));
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Code points XML 1.0 has no spelling for: most control characters, the
3
+ * surrogate halves, and the two non-characters at the end of the basic plane.
4
+ * They are dropped rather than escaped, because there is no escape — a
5
+ * document containing one is not merely ugly, it does not parse.
6
+ *
7
+ * Labels reach here from an extraction model by way of the schema, which
8
+ * constrains their length but not their alphabet, so this is the last place
9
+ * such a character can be stopped. Matching by code point rather than by code
10
+ * unit is what keeps a well-formed astral character — which is a pair of
11
+ * surrogates in memory — from being mistaken for two lone halves.
12
+ */
13
+ const FORBIDDEN_IN_XML =
14
+ /[\u0000-\u0008\u000B\u000C\u000E-\u001F\uD800-\uDFFF\uFFFE\uFFFF]/gu;
15
+
16
+ /**
17
+ * XML escaping for text nodes and attribute values alike. Attributes are
18
+ * always written in double quotes, so the single quote does not need an
19
+ * entity, but `<` and `&` do in both positions.
20
+ */
21
+ export const escapeXml = (value: string): string =>
22
+ value
23
+ .replace(FORBIDDEN_IN_XML, "")
24
+ .replace(/&/g, "&amp;")
25
+ .replace(/</g, "&lt;")
26
+ .replace(/>/g, "&gt;")
27
+ .replace(/"/g, "&quot;");
28
+
29
+ export type Attributes = Readonly<Record<string, string | number | undefined>>;
30
+
31
+ const serialiseAttributes = (attributes: Attributes): string =>
32
+ Object.entries(attributes)
33
+ .filter((entry): entry is [string, string | number] => entry[1] !== undefined)
34
+ .map(([name, value]) => ` ${name}="${escapeXml(String(value))}"`)
35
+ .join("");
36
+
37
+ export const tag = (name: string, attributes: Attributes): string =>
38
+ `<${name}${serialiseAttributes(attributes)}/>`;
39
+
40
+ export const wrap = (name: string, attributes: Attributes, children: string): string =>
41
+ `<${name}${serialiseAttributes(attributes)}>${children}</${name}>`;
42
+
43
+ export const textNode = (
44
+ attributes: Attributes,
45
+ content: string,
46
+ ): string => wrap("text", attributes, escapeXml(content));
47
+
48
+ /** Joins emitted fragments with newlines, dropping the ones that produced nothing. */
49
+ export const lines = (fragments: readonly string[]): string =>
50
+ fragments.filter((fragment) => fragment.length > 0).join("\n");
@@ -0,0 +1,48 @@
1
+ import { HERO_PULSE_DURATION, PULSE_DURATION } from "../design.js";
2
+ import { coord } from "../geometry.js";
3
+ import { lines, tag, wrap } from "./primitives.js";
4
+
5
+ /**
6
+ * The travelling pulse: the mark that says a connection carries traffic
7
+ * rather than merely existing. Both lenses draw it from here, because a
8
+ * reader moving between them is reading one language — an architecture edge
9
+ * and a sequence message differ in what they connect, not in how they live.
10
+ *
11
+ * A line running behind the drawing's clock says so with a negative `begin`,
12
+ * which starts its motion that far into its own turn. A positive delay would
13
+ * describe the same steady state and lie for the seconds after load: an
14
+ * animation has no effect before it begins, so the dot waiting for its turn
15
+ * would sit at the canvas origin, in the corner, in full view.
16
+ */
17
+ /** The dot itself, so both lenses draw the same mark whatever clock moves it. */
18
+ export const PULSE_RADIUS = 2.6;
19
+ export const TRAIN_RADIUS = 3;
20
+
21
+ export const travellingPulses = (pulse: {
22
+ path: string;
23
+ colour: string;
24
+ /** Dots riding this line at once, spread evenly around the turn. */
25
+ count: number;
26
+ /** How far this line runs behind the drawing's clock, in seconds. */
27
+ lag: number;
28
+ }): string => {
29
+ const { path, colour, count, lag } = pulse;
30
+ const train = count > 1;
31
+ const duration = train ? HERO_PULSE_DURATION : PULSE_DURATION;
32
+
33
+ return lines(
34
+ Array.from({ length: count }, (_, index) => {
35
+ const behind = (lag + (duration / count) * index) % duration;
36
+ return wrap(
37
+ "circle",
38
+ { r: train ? TRAIN_RADIUS : PULSE_RADIUS, fill: colour },
39
+ tag("animateMotion", {
40
+ dur: `${coord(duration)}s`,
41
+ begin: behind === 0 ? undefined : `${coord(behind - duration)}s`,
42
+ repeatCount: "indefinite",
43
+ path,
44
+ }),
45
+ );
46
+ }),
47
+ );
48
+ };
package/src/text.ts ADDED
@@ -0,0 +1,92 @@
1
+ import { assertNever } from "@coldtea/pr-lens-schema";
2
+
3
+ /**
4
+ * The renderer never asks a font engine how wide a string is: it must produce
5
+ * the same geometry on a CI runner with no fonts installed as on a laptop
6
+ * with all of them. Widths come from a table instead, and every box is sized
7
+ * with enough slack that the small error against a real face stays invisible.
8
+ */
9
+ export type Face = "sans" | "sans-bold" | "mono";
10
+
11
+ export const SANS_STACK =
12
+ '-apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif';
13
+ export const MONO_STACK =
14
+ 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace';
15
+
16
+ /** Advance widths per 1000 units of em, for the regular sans face. */
17
+ const SANS_ADVANCE: Readonly<Record<string, number>> = {
18
+ " ": 278, "!": 278, '"': 355, "#": 556, $: 556, "%": 889, "&": 667, "'": 191,
19
+ "(": 333, ")": 333, "*": 389, "+": 584, ",": 278, "-": 333, ".": 278, "/": 278,
20
+ "0": 556, "1": 556, "2": 556, "3": 556, "4": 556, "5": 556, "6": 556, "7": 556,
21
+ "8": 556, "9": 556, ":": 278, ";": 278, "<": 584, "=": 584, ">": 584, "?": 556,
22
+ "@": 1015, A: 667, B: 667, C: 722, D: 722, E: 667, F: 611, G: 778, H: 722,
23
+ I: 278, J: 500, K: 667, L: 556, M: 833, N: 722, O: 778, P: 667, Q: 778, R: 722,
24
+ S: 667, T: 611, U: 722, V: 667, W: 944, X: 667, Y: 667, Z: 611, "[": 278,
25
+ "\\": 278, "]": 278, "^": 469, _: 556, "`": 333, a: 556, b: 556, c: 500, d: 556,
26
+ e: 556, f: 278, g: 556, h: 556, i: 222, j: 222, k: 500, l: 222, m: 833, n: 556,
27
+ o: 556, p: 556, q: 556, r: 333, s: 500, t: 278, u: 556, v: 500, w: 722, x: 500,
28
+ y: 500, z: 500, "{": 334, "|": 260, "}": 334, "~": 584,
29
+ };
30
+
31
+ /** What a character outside the table is assumed to cost, in the same units. */
32
+ const FALLBACK_ADVANCE = 600;
33
+
34
+ /**
35
+ * The bold face of the same family runs a little wider at every weight step.
36
+ * One factor over the regular table is close enough for boxes that already
37
+ * carry padding, and it keeps a second table from drifting out of sync.
38
+ */
39
+ const BOLD_WIDENING = 1.06;
40
+
41
+ const MONO_ADVANCE = 600;
42
+
43
+ const advanceFor = (face: Face, character: string): number => {
44
+ switch (face) {
45
+ case "sans":
46
+ return SANS_ADVANCE[character] ?? FALLBACK_ADVANCE;
47
+ case "sans-bold":
48
+ return (SANS_ADVANCE[character] ?? FALLBACK_ADVANCE) * BOLD_WIDENING;
49
+ case "mono":
50
+ return MONO_ADVANCE;
51
+ default:
52
+ return assertNever(face, "Unhandled font face");
53
+ }
54
+ };
55
+
56
+ /** Width of `text` in user units at `fontSize`, to a hundredth of a unit. */
57
+ export const measure = (text: string, face: Face, fontSize: number): number => {
58
+ let thousandths = 0;
59
+ for (const character of text) thousandths += advanceFor(face, character);
60
+ return Math.round((thousandths / 1000) * fontSize * 100) / 100;
61
+ };
62
+
63
+ const ELLIPSIS = "…";
64
+
65
+ /**
66
+ * Shortens `text` until it fits `maxWidth`, ellipsis included. Cutting by
67
+ * code point rather than by UTF-16 unit keeps a surrogate pair from being
68
+ * split into a replacement character.
69
+ */
70
+ export const truncate = (
71
+ text: string,
72
+ face: Face,
73
+ fontSize: number,
74
+ maxWidth: number,
75
+ ): string => {
76
+ if (measure(text, face, fontSize) <= maxWidth) return text;
77
+
78
+ const characters = [...text];
79
+ const ellipsisWidth = measure(ELLIPSIS, face, fontSize);
80
+ let width = 0;
81
+ let kept = 0;
82
+
83
+ for (const character of characters) {
84
+ const next = width + measure(character, face, fontSize);
85
+ if (next + ellipsisWidth > maxWidth) break;
86
+ width = next;
87
+ kept += 1;
88
+ }
89
+
90
+ if (kept === 0) return ELLIPSIS;
91
+ return `${characters.slice(0, kept).join("").trimEnd()}${ELLIPSIS}`;
92
+ };
package/src/theme.ts ADDED
@@ -0,0 +1,107 @@
1
+ import { assertNever, THEMES, type Theme } from "@coldtea/pr-lens-schema";
2
+
3
+ /**
4
+ * The two halves of a GitHub `<picture>` pair. A comment ships both and the
5
+ * client picks; neither half may depend on the page it lands in, because an
6
+ * SVG served as an image cannot see it.
7
+ *
8
+ * The contract owns the pair, because the number of themes is one half of the
9
+ * arithmetic that bounds a document's view tree. Re-exported here so a caller
10
+ * rendering a diagram does not have to reach into the schema for it.
11
+ */
12
+ export { THEMES, type Theme };
13
+
14
+ /**
15
+ * Every colour the renderer can paint, resolved to a literal before it
16
+ * reaches the document. CSS custom properties would be shorter, but GitHub
17
+ * serves these files through an image proxy where nothing outside the file
18
+ * exists, so the palette is baked in per theme.
19
+ */
20
+ export type Palette = {
21
+ background: string;
22
+ dot: string;
23
+ lane: string;
24
+ card: string;
25
+ cardBorder: string;
26
+ foreground: string;
27
+ muted: string;
28
+ edge: string;
29
+ chip: string;
30
+ pill: string;
31
+ pillBorder: string;
32
+ lifeline: string;
33
+ added: string;
34
+ addedFill: string;
35
+ addedBorder: string;
36
+ modified: string;
37
+ modifiedFill: string;
38
+ modifiedBorder: string;
39
+ removed: string;
40
+ removedFill: string;
41
+ removedBorder: string;
42
+ neutralFill: string;
43
+ shadow: string;
44
+ };
45
+
46
+ const LIGHT: Palette = {
47
+ background: "#ffffff",
48
+ dot: "rgba(140,149,159,.35)",
49
+ lane: "#f6f8fa",
50
+ card: "#ffffff",
51
+ cardBorder: "#d1d9e0",
52
+ foreground: "#1f2328",
53
+ muted: "#59636e",
54
+ edge: "#8c959f",
55
+ chip: "#f6f8fa",
56
+ pill: "#ffffff",
57
+ pillBorder: "#d8dee4",
58
+ lifeline: "#d1d9e0",
59
+ added: "#1a7f37",
60
+ addedFill: "#dafbe1",
61
+ addedBorder: "rgba(31,136,61,.4)",
62
+ modified: "#9a6700",
63
+ modifiedFill: "#fff8c5",
64
+ modifiedBorder: "rgba(154,103,0,.35)",
65
+ removed: "#d1242f",
66
+ removedFill: "#ffebe9",
67
+ removedBorder: "rgba(209,36,47,.35)",
68
+ neutralFill: "#f6f8fa",
69
+ shadow: "rgba(31,35,40,.14)",
70
+ };
71
+
72
+ const DARK: Palette = {
73
+ background: "#0d1117",
74
+ dot: "rgba(110,118,129,.22)",
75
+ lane: "rgba(110,118,129,.07)",
76
+ card: "#1c2128",
77
+ cardBorder: "#3d444d",
78
+ foreground: "#e6edf3",
79
+ muted: "#9198a1",
80
+ edge: "#6e7681",
81
+ chip: "rgba(110,118,129,.18)",
82
+ pill: "#0d1117",
83
+ pillBorder: "#21262d",
84
+ lifeline: "#30363d",
85
+ added: "#3fb950",
86
+ addedFill: "rgba(46,160,67,.15)",
87
+ addedBorder: "rgba(63,185,80,.4)",
88
+ modified: "#d29922",
89
+ modifiedFill: "rgba(187,128,9,.15)",
90
+ modifiedBorder: "rgba(210,153,34,.4)",
91
+ removed: "#f85149",
92
+ removedFill: "rgba(248,81,73,.12)",
93
+ removedBorder: "rgba(248,81,73,.4)",
94
+ neutralFill: "rgba(110,118,129,.18)",
95
+ shadow: "rgba(0,0,0,.28)",
96
+ };
97
+
98
+ export const paletteFor = (theme: Theme): Palette => {
99
+ switch (theme) {
100
+ case "light":
101
+ return LIGHT;
102
+ case "dark":
103
+ return DARK;
104
+ default:
105
+ return assertNever(theme, "Unhandled theme");
106
+ }
107
+ };
package/src/version.ts ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Stamped into every render manifest so a stored diagram can be traced to the
3
+ * code that drew it. Kept in step with package.json by a test, since a
4
+ * package cannot read its own manifest without doing I/O the renderer does
5
+ * not otherwise need.
6
+ */
7
+ export const RENDERER_NAME = "@coldtea/pr-lens-renderer";
8
+
9
+ export const RENDERER_VERSION = "0.1.0";