@coldtea/pr-lens-renderer 0.2.3 → 0.2.5

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.
@@ -1,5 +1,16 @@
1
- import type { Flow, FlowMessage, GraphNode, MessageKind } from "@coldtea/pr-lens-schema";
2
1
  import { assertNever } from "@coldtea/pr-lens-schema";
2
+ import type { Flow, FlowMessage, GraphNode, MessageKind } from "@coldtea/pr-lens-schema";
3
+ import { measure } from "../text.js";
4
+ import { coord } from "../geometry.js";
5
+ import type { Box } from "../geometry.js";
6
+ import type { Palette } from "../theme.js";
7
+ import { lines, tag, wrap } from "./primitives.js";
8
+ import { PULSE_RADIUS, TRAIN_RADIUS } from "./pulse.js";
9
+ import { messageAttributes, stylesFor } from "./styles.js";
10
+ import { paintCard, paintLabelPill } from "./architecture.js";
11
+ import { atlasBoxes, emptyAtlas, type RenderAtlas } from "../atlas.js";
12
+ import { canvasFor, covering, union, type Canvas } from "../bounds.js";
13
+ import { markerFor, openMarkerFor, shifted, toneColour, toneFor, type Tone } from "./document.js";
3
14
  import {
4
15
  DIAGRAM_MARGIN,
5
16
  FLOW_CYCLE_MAX,
@@ -12,11 +23,6 @@ import {
12
23
  PILL_TEXT_SIZE,
13
24
  TITLE_SIZE,
14
25
  } from "../design.js";
15
- import { atlasBoxes, emptyAtlas, type RenderAtlas } from "../atlas.js";
16
- import { canvasFor, covering, union, type Canvas } from "../bounds.js";
17
- import type { Box } from "../geometry.js";
18
- import { measure } from "../text.js";
19
- import { coord } from "../geometry.js";
20
26
  import {
21
27
  ACTIVATION_HALF_WIDTH,
22
28
  FLOW_BAND_PAD_X,
@@ -32,11 +38,6 @@ import {
32
38
  type FlowLayout,
33
39
  type PlacedMessage,
34
40
  } from "../layout/dataflow.js";
35
- import type { Palette } from "../theme.js";
36
- import { paintCard, paintLabelPill } from "./architecture.js";
37
- import { markerFor, openMarkerFor, shifted, toneColour, toneFor, type Tone } from "./document.js";
38
- import { lines, tag, wrap } from "./primitives.js";
39
- import { PULSE_RADIUS, TRAIN_RADIUS } from "./pulse.js";
40
41
 
41
42
  /** A ratio inside the animation cycle, written to a fixed number of places. */
42
43
  const ratio = (value: number): string => String(Math.round(value * 10000) / 10000);
@@ -236,16 +237,17 @@ const paintMessage = (
236
237
  const tone = toneFor(placed.message.delta);
237
238
  const direction = travelDirection(placed.message.kind, placed.fromX, placed.toX);
238
239
  const head = headFor(placed.message.kind, tone);
240
+ const attributes = messageAttributes(placed.message, palette);
239
241
 
240
242
  if (direction === 0) {
241
243
  const activated = activeAt(placed.message.from, placed.y);
242
244
  const path = selfPath(placed.fromX, placed.y, activated);
243
245
  return {
244
246
  line: lines([
245
- tag("path", { class: messageClasses(placed.message, tone), d: path, "marker-end": head }),
247
+ tag("path", { class: messageClasses(placed.message, tone), d: path, "marker-end": head, ...attributes }),
246
248
  pulsesFor(placed, path, slotCount, palette),
247
249
  ]),
248
- pill: paintLabelPill(placed.label, selfPillBox(placed, activated), tone),
250
+ pill: paintLabelPill(placed.label, selfPillBox(placed, activated), tone, palette),
249
251
  };
250
252
  }
251
253
 
@@ -254,10 +256,10 @@ const paintMessage = (
254
256
 
255
257
  return {
256
258
  line: lines([
257
- tag("path", { class: messageClasses(placed.message, tone), d: path, "marker-end": head }),
259
+ tag("path", { class: messageClasses(placed.message, tone), d: path, "marker-end": head, ...attributes }),
258
260
  pulsesFor(placed, path, slotCount, palette),
259
261
  ]),
260
- pill: paintLabelPill(placed.label, pillBox(placed, ends), tone),
262
+ pill: paintLabelPill(placed.label, pillBox(placed, ends), tone, palette),
261
263
  };
262
264
  };
263
265
 
@@ -283,6 +285,7 @@ const paintFlow = (
283
285
  palette: Palette,
284
286
  ): string => {
285
287
  const activeAt = activationLookup(layout);
288
+ const styles = stylesFor(palette);
286
289
 
287
290
  const lifelineBottom = layout.top + layout.height;
288
291
 
@@ -295,6 +298,7 @@ const paintFlow = (
295
298
  width: coord(box.width),
296
299
  height: coord(box.height),
297
300
  rx: LANE_RADIUS,
301
+ ...styles.lane,
298
302
  });
299
303
  });
300
304
 
@@ -306,6 +310,7 @@ const paintFlow = (
306
310
  y1: coord(layout.lifelineTop),
307
311
  x2: coord(participant.centreX),
308
312
  y2: coord(lifelineBottom),
313
+ ...styles.lifeline,
309
314
  }),
310
315
  ...participant.activations.map((bar) =>
311
316
  tag("rect", {
@@ -315,20 +320,24 @@ const paintFlow = (
315
320
  width: ACTIVATION_HALF_WIDTH * 2,
316
321
  height: coord(bar.bottom - bar.top),
317
322
  rx: 4,
323
+ ...styles.activation,
318
324
  }),
319
325
  ),
320
326
  ]),
321
327
  );
322
328
 
323
329
  const cards = layout.participants.map((participant, index) =>
324
- paintCard({
325
- node: participant.node,
326
- box: participant.card,
327
- showIcon: true,
328
- titleSize: TITLE_SIZE,
329
- row: 0,
330
- laneIndex: index,
331
- }),
330
+ paintCard(
331
+ {
332
+ node: participant.node,
333
+ box: participant.card,
334
+ showIcon: true,
335
+ titleSize: TITLE_SIZE,
336
+ row: 0,
337
+ laneIndex: index,
338
+ },
339
+ palette,
340
+ ),
332
341
  );
333
342
 
334
343
  const lineMarkup: string[] = [];
@@ -1,9 +1,9 @@
1
1
  import type { Delta } from "@coldtea/pr-lens-schema";
2
2
  import { assertNever } from "@coldtea/pr-lens-schema";
3
- import type { Canvas } from "../bounds.js";
4
3
  import { coord } from "../geometry.js";
4
+ import { SANS_STACK } from "../text.js";
5
+ import type { Canvas } from "../bounds.js";
5
6
  import type { Palette } from "../theme.js";
6
- import { MONO_STACK, SANS_STACK } from "../text.js";
7
7
  import { escapeXml, lines, tag, wrap } from "./primitives.js";
8
8
 
9
9
  /**
@@ -45,55 +45,6 @@ export const toneColour = (palette: Palette, tone: Tone): string => {
45
45
 
46
46
  const TONES = ["added", "modified", "removed", "neutral"] as const satisfies readonly Tone[];
47
47
 
48
- const stylesheet = (palette: Palette): string =>
49
- [
50
- `text{font-family:${SANS_STACK}}`,
51
- `.lanebox{fill:${palette.lane}}`,
52
- `.lanelabel{font-size:10px;font-weight:700;letter-spacing:.12em;fill:${palette.muted}}`,
53
- `.card{fill:${palette.card};stroke:${palette.cardBorder};stroke-width:1}`,
54
- `.card-added{stroke:${palette.added};stroke-opacity:.55}`,
55
- `.card-modified{stroke:${palette.modified};stroke-opacity:.5}`,
56
- `.ghost{opacity:.55}`,
57
- `.ghost .card{stroke:${palette.removed};stroke-dasharray:4 3;stroke-opacity:.6}`,
58
- `.context{opacity:.82}`,
59
- `.ntitle{font-weight:600;fill:${palette.foreground}}`,
60
- `.strike{text-decoration:line-through}`,
61
- `.nsub{font-size:9.5px;fill:${palette.muted};font-family:${MONO_STACK}}`,
62
- `.chip{fill:${palette.chip}}`,
63
- `.glyph{fill:${palette.muted}}`,
64
- `.glyph-stroke{stroke:${palette.muted};stroke-width:1.4;fill:none}`,
65
- `.bdg text{font-size:8.5px;font-weight:700;letter-spacing:.06em}`,
66
- `.bdg rect{stroke-width:1}`,
67
- `.bdg-added rect{fill:${palette.addedFill};stroke:${palette.addedBorder}}`,
68
- `.bdg-added text{fill:${palette.addedText}}`,
69
- `.bdg-modified rect{fill:${palette.modifiedFill};stroke:${palette.modifiedBorder}}`,
70
- `.bdg-modified text{fill:${palette.modifiedText}}`,
71
- `.bdg-removed rect{fill:${palette.removedFill};stroke:${palette.removedBorder}}`,
72
- `.bdg-removed text{fill:${palette.removedText}}`,
73
- `.bdg-neutral rect{fill:${palette.neutralFill};stroke:${palette.cardBorder}}`,
74
- `.bdg-neutral text{fill:${palette.muted}}`,
75
- `.edge{fill:none;stroke-width:1.5}`,
76
- `.edge-added{stroke:${palette.added}}`,
77
- `.edge-modified{stroke:${palette.modified}}`,
78
- `.edge-removed{stroke:${palette.removed};stroke-dasharray:5 4;opacity:.7}`,
79
- `.edge-neutral{stroke:${palette.edge}}`,
80
- `.hero{stroke-width:2.25}`,
81
- `.faded{opacity:.45}`,
82
- `.glow{fill:none;stroke-width:7;opacity:.14}`,
83
- `.msg-self{font-size:11px;fill:${palette.foreground}}`,
84
- `.lpill{fill:${palette.pill};stroke:${palette.pillBorder};stroke-width:1}`,
85
- `.ltext{font-size:9.5px;font-weight:600;fill:${palette.muted}}`,
86
- `.ltext-added{fill:${palette.addedText}}`,
87
- `.ltext-modified{fill:${palette.modifiedText}}`,
88
- `.ltext-removed{fill:${palette.removedText}}`,
89
- `.cardsh{filter:drop-shadow(0 1px 2px ${palette.shadow})}`,
90
- `.lifeline{stroke:${palette.lifeline};stroke-width:1;stroke-dasharray:3 4}`,
91
- `.actbar{fill:${palette.addedFill};stroke:${palette.addedBorder}}`,
92
- `.msg{fill:none;stroke-width:1.5}`,
93
- `.msg-return{stroke-dasharray:4 3;opacity:.8}`,
94
- `.msg-strong{stroke-width:2.25}`,
95
- ].join("");
96
-
97
48
  /**
98
49
  * Two arrowhead forms per tone, the classic sequence-diagram pair: a filled
99
50
  * head for a message the sender waits on, an open line-form head for one it
@@ -174,10 +125,10 @@ export const svgDocument = (input: {
174
125
 
175
126
  return lines([
176
127
  `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${coord(width)} ${coord(height)}" ` +
177
- `width="${coord(width)}" height="${coord(height)}" role="img" aria-label="${escapeXml(title)}">`,
128
+ `width="${coord(width)}" height="${coord(height)}" role="img" aria-label="${escapeXml(title)}" ` +
129
+ `font-family="${escapeXml(SANS_STACK)}">`,
178
130
  wrap("title", {}, escapeXml(title)),
179
131
  description === undefined ? "" : wrap("desc", {}, escapeXml(description)),
180
- wrap("style", {}, stylesheet(palette)),
181
132
  defs,
182
133
  tag("rect", { x: 0, y: 0, width: coord(width), height: coord(height), rx: 12, fill: palette.background }),
183
134
  tag("rect", { x: 0, y: 0, width: coord(width), height: coord(height), rx: 12, fill: "url(#dots)" }),
package/src/svg/icons.ts CHANGED
@@ -1,33 +1,23 @@
1
- import type { NodeKind } from "@coldtea/pr-lens-schema";
2
1
  import { assertNever } from "@coldtea/pr-lens-schema";
2
+ import type { NodeKind } from "@coldtea/pr-lens-schema";
3
3
  import { coord } from "../geometry.js";
4
4
  import { MONO_STACK } from "../text.js";
5
+ import type { SvgStyles } from "./styles.js";
6
+ import type { Attributes } from "./primitives.js";
5
7
  import { lines, tag, textNode, wrap } from "./primitives.js";
6
8
 
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 =>
9
+ const glyphText = (cx: number, cy: number, content: string, attributes: Attributes): string =>
10
10
  textNode(
11
11
  {
12
12
  class: "glyph",
13
13
  x: coord(cx),
14
14
  y: coord(cy + 4.5),
15
15
  "text-anchor": "middle",
16
- style,
16
+ ...attributes,
17
17
  },
18
18
  content,
19
19
  );
20
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
21
  /**
32
22
  * The chip glyph for a node's kind, centred on a point.
33
23
  *
@@ -35,12 +25,26 @@ const box = (cx: number, cy: number, width: number, height: number, radius?: num
35
25
  * is why the kind enum is coarse. Every kind draws something: a card with an
36
26
  * empty chip reads as a rendering fault rather than as an unknown kind.
37
27
  */
38
- const kindGlyph = (kind: NodeKind, cx: number, cy: number): string => {
28
+ const kindGlyph = (kind: NodeKind, cx: number, cy: number, styles: SvgStyles): string => {
29
+ const stroked = (path: string): string =>
30
+ tag("path", { class: "glyph-stroke", d: path, ...styles.glyphStroke });
31
+
32
+ const box = (cx: number, cy: number, width: number, height: number, radius?: number): string =>
33
+ tag("rect", {
34
+ class: "glyph-stroke",
35
+ x: coord(cx - width / 2),
36
+ y: coord(cy - height / 2),
37
+ width: coord(width),
38
+ height: coord(height),
39
+ rx: radius === undefined ? undefined : coord(radius),
40
+ ...styles.glyphStroke,
41
+ });
42
+
39
43
  switch (kind) {
40
44
  case "service":
41
45
  return lines([
42
46
  box(cx, cy, 13, 13, 3),
43
- tag("circle", { class: "glyph", cx: coord(cx), cy: coord(cy), r: 2 }),
47
+ tag("circle", { class: "glyph", ...styles.glyph, cx: coord(cx), cy: coord(cy), r: 2 }),
44
48
  ]);
45
49
  case "app":
46
50
  return lines([
@@ -48,17 +52,23 @@ const kindGlyph = (kind: NodeKind, cx: number, cy: number): string => {
48
52
  stroked(`M${coord(cx - 7)},${coord(cy - 2)} h14`),
49
53
  ]);
50
54
  case "module":
51
- return glyphText(cx, cy, "{ }", `font-size:11px;font-family:${MONO_STACK}`);
55
+ return glyphText(cx, cy, "{ }", { ...styles.glyph, "font-size": 11, "font-family": MONO_STACK });
52
56
  case "function":
53
- return glyphText(cx, cy, "ƒ", "font-size:14px;font-style:italic;font-family:Georgia,serif");
57
+ return glyphText(cx, cy, "ƒ", {
58
+ ...styles.glyph,
59
+ "font-size": 14,
60
+ "font-style": "italic",
61
+ "font-family": "Georgia,serif",
62
+ });
54
63
  case "route":
55
64
  return tag("path", {
56
65
  class: "glyph",
66
+ ...styles.glyph,
57
67
  d: `M${coord(cx - 7)},${coord(cy - 6)} L${coord(cx + 7)},${coord(cy)} L${coord(cx - 7)},${coord(cy + 6)} Z`,
58
68
  });
59
69
  case "job":
60
70
  return lines([
61
- tag("circle", { class: "glyph-stroke", cx: coord(cx), cy: coord(cy), r: 6.5 }),
71
+ tag("circle", { class: "glyph-stroke", ...styles.glyphStroke, cx: coord(cx), cy: coord(cy), r: 6.5 }),
62
72
  stroked(`M${coord(cx)},${coord(cy - 3.5)} V${coord(cy)} H${coord(cx + 3)}`),
63
73
  ]);
64
74
  case "queue":
@@ -69,7 +79,7 @@ const kindGlyph = (kind: NodeKind, cx: number, cy: number): string => {
69
79
  ]);
70
80
  case "datastore":
71
81
  return lines([
72
- tag("ellipse", { class: "glyph-stroke", cx: coord(cx), cy: coord(cy - 4.5), rx: 6.5, ry: 2.6 }),
82
+ tag("ellipse", { class: "glyph-stroke", ...styles.glyphStroke, cx: coord(cx), cy: coord(cy - 4.5), rx: 6.5, ry: 2.6 }),
73
83
  stroked(
74
84
  `M${coord(cx - 6.5)},${coord(cy - 4.5)} v9 a6.5,2.6 0 0 0 13 0 v-9`,
75
85
  ),
@@ -77,6 +87,7 @@ const kindGlyph = (kind: NodeKind, cx: number, cy: number): string => {
77
87
  case "cache":
78
88
  return tag("path", {
79
89
  class: "glyph",
90
+ ...styles.glyph,
80
91
  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
92
  });
82
93
  case "external":
@@ -93,8 +104,8 @@ const kindGlyph = (kind: NodeKind, cx: number, cy: number): string => {
93
104
  return lines([
94
105
  stroked(`M${coord(cx - 7)},${coord(cy - 4)} h14`),
95
106
  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 }),
107
+ tag("circle", { class: "glyph", ...styles.glyph, cx: coord(cx - 2), cy: coord(cy - 4), r: 2.2 }),
108
+ tag("circle", { class: "glyph", ...styles.glyph, cx: coord(cx + 3), cy: coord(cy + 4), r: 2.2 }),
98
109
  ]);
99
110
  case "test":
100
111
  return stroked(`M${coord(cx - 6)},${coord(cy)} l4,4 l8,-8`);
@@ -108,14 +119,14 @@ const kindGlyph = (kind: NodeKind, cx: number, cy: number): string => {
108
119
  ]);
109
120
  case "other":
110
121
  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 }),
122
+ tag("circle", { class: "glyph", ...styles.glyph, cx: coord(cx - 5), cy: coord(cy), r: 1.8 }),
123
+ tag("circle", { class: "glyph", ...styles.glyph, cx: coord(cx), cy: coord(cy), r: 1.8 }),
124
+ tag("circle", { class: "glyph", ...styles.glyph, cx: coord(cx + 5), cy: coord(cy), r: 1.8 }),
114
125
  ]);
115
126
  default:
116
127
  return assertNever(kind, "Unhandled node kind");
117
128
  }
118
129
  };
119
130
 
120
- export const glyphGroup = (kind: NodeKind, cx: number, cy: number): string =>
121
- wrap("g", {}, kindGlyph(kind, cx, cy));
131
+ export const glyphGroup = (kind: NodeKind, cx: number, cy: number, styles: SvgStyles): string =>
132
+ wrap("g", {}, kindGlyph(kind, cx, cy, styles));
@@ -0,0 +1,122 @@
1
+ import { assertNever } from "@coldtea/pr-lens-schema";
2
+ import type { Delta, FlowMessage, GraphEdge } from "@coldtea/pr-lens-schema";
3
+ import { MONO_STACK } from "../text.js";
4
+ import type { Palette } from "../theme.js";
5
+ import type { Attributes } from "./primitives.js";
6
+ import { toneColour, toneFor, type Tone } from "./document.js";
7
+
8
+ // GitHub's attachment viewer can ignore CSS, so appearance lives on the elements.
9
+ export const stylesFor = (palette: Palette) => ({
10
+ lane: { fill: palette.lane },
11
+ laneLabel: { "font-size": 10, "font-weight": 700, "letter-spacing": ".12em", fill: palette.muted },
12
+ title: { "font-weight": 600, fill: palette.foreground },
13
+ subtitle: { "font-size": 9.5, fill: palette.muted, "font-family": MONO_STACK },
14
+ chip: { fill: palette.chip },
15
+ glyph: { fill: palette.muted },
16
+ glyphStroke: { stroke: palette.muted, "stroke-width": 1.4, fill: "none" },
17
+ badgeText: { "font-size": 8.5, "font-weight": 700, "letter-spacing": ".06em" },
18
+ glow: { fill: "none", "stroke-width": 7, opacity: 0.14 },
19
+ pill: { fill: palette.pill, stroke: palette.pillBorder, "stroke-width": 1 },
20
+ pillText: { "font-size": 9.5, "font-weight": 600 },
21
+ lifeline: { stroke: palette.lifeline, "stroke-width": 1, "stroke-dasharray": "3 4" },
22
+ activation: { fill: palette.addedFill, stroke: palette.addedBorder },
23
+ });
24
+
25
+ export type SvgStyles = ReturnType<typeof stylesFor>;
26
+
27
+ const cardStroke = (delta: Delta, palette: Palette): Attributes => {
28
+ switch (delta) {
29
+ case "added":
30
+ return { stroke: palette.added, "stroke-opacity": 0.55 };
31
+ case "modified":
32
+ return { stroke: palette.modified, "stroke-opacity": 0.5 };
33
+ case "removed":
34
+ return { stroke: palette.removed, "stroke-dasharray": "4 3", "stroke-opacity": 0.6 };
35
+ case "unchanged":
36
+ return { stroke: palette.cardBorder };
37
+ default:
38
+ return assertNever(delta);
39
+ }
40
+ };
41
+
42
+ export const cardAttributes = (delta: Delta, palette: Palette): Attributes => ({
43
+ fill: palette.card,
44
+ "stroke-width": 1,
45
+ ...cardStroke(delta, palette),
46
+ });
47
+
48
+ const cardOpacity = (delta: Delta): number | undefined => {
49
+ switch (delta) {
50
+ case "removed":
51
+ return 0.55;
52
+ case "unchanged":
53
+ return 0.82;
54
+ case "added":
55
+ case "modified":
56
+ return undefined;
57
+ default:
58
+ return assertNever(delta);
59
+ }
60
+ };
61
+
62
+ export const cardGroupAttributes = (delta: Delta, palette: Palette): Attributes => ({
63
+ filter: `drop-shadow(0 1px 2px ${palette.shadow})`,
64
+ opacity: cardOpacity(delta),
65
+ });
66
+
67
+ export const badgeColours = (tone: Tone, palette: Palette) => {
68
+ switch (tone) {
69
+ case "added":
70
+ return { fill: palette.addedFill, stroke: palette.addedBorder, text: palette.addedText };
71
+ case "modified":
72
+ return { fill: palette.modifiedFill, stroke: palette.modifiedBorder, text: palette.modifiedText };
73
+ case "removed":
74
+ return { fill: palette.removedFill, stroke: palette.removedBorder, text: palette.removedText };
75
+ case "neutral":
76
+ return { fill: palette.neutralFill, stroke: palette.cardBorder, text: palette.muted };
77
+ default:
78
+ return assertNever(tone);
79
+ }
80
+ };
81
+
82
+ const strokeAttributes = (delta: Delta, palette: Palette): Attributes => ({
83
+ fill: "none",
84
+ stroke: toneColour(palette, toneFor(delta)),
85
+ "stroke-width": 1.5,
86
+ "stroke-dasharray": delta === "removed" ? "5 4" : undefined,
87
+ opacity: delta === "removed" ? 0.7 : undefined,
88
+ });
89
+
90
+ export const edgeAttributes = (edge: GraphEdge, palette: Palette): Attributes => {
91
+ const attributes = {
92
+ ...strokeAttributes(edge.delta, palette),
93
+ ...(edge.delta === "unchanged" ? { opacity: 0.82 } : {}),
94
+ };
95
+ switch (edge.emphasis) {
96
+ case "hero":
97
+ return { ...attributes, "stroke-width": 2.25 };
98
+ case "muted":
99
+ return { ...attributes, opacity: 0.45 };
100
+ case "normal":
101
+ return attributes;
102
+ default:
103
+ return assertNever(edge.emphasis);
104
+ }
105
+ };
106
+
107
+ export const messageAttributes = (message: FlowMessage, palette: Palette): Attributes => {
108
+ const attributes = {
109
+ ...strokeAttributes(message.delta, palette),
110
+ "stroke-width": message.delta === "added" ? 2.25 : 1.5,
111
+ };
112
+ switch (message.kind) {
113
+ case "return":
114
+ return { ...attributes, "stroke-dasharray": "4 3", opacity: 0.8 };
115
+ case "sync":
116
+ case "async":
117
+ case "self":
118
+ return attributes;
119
+ default:
120
+ return assertNever(message.kind);
121
+ }
122
+ };
package/src/version.ts CHANGED
@@ -6,4 +6,4 @@
6
6
  */
7
7
  export const RENDERER_NAME = "@coldtea/pr-lens-renderer";
8
8
 
9
- export const RENDERER_VERSION = "0.2.3";
9
+ export const RENDERER_VERSION = "0.2.5";