@coldtea/pr-lens-renderer 0.2.6 → 0.3.1
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.
- package/README.md +2 -1
- package/dist/bounds.d.ts +6 -1
- package/dist/bounds.d.ts.map +1 -1
- package/dist/bounds.js +21 -0
- package/dist/bounds.js.map +1 -1
- package/dist/design.d.ts +5 -0
- package/dist/design.d.ts.map +1 -1
- package/dist/design.js +5 -0
- package/dist/design.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/layout/architecture.d.ts +2 -0
- package/dist/layout/architecture.d.ts.map +1 -1
- package/dist/layout/architecture.js +5 -0
- package/dist/layout/architecture.js.map +1 -1
- package/dist/layout/congestion.d.ts +5 -0
- package/dist/layout/congestion.d.ts.map +1 -1
- package/dist/layout/congestion.js +50 -12
- package/dist/layout/congestion.js.map +1 -1
- package/dist/layout/edges.d.ts +18 -3
- package/dist/layout/edges.d.ts.map +1 -1
- package/dist/layout/edges.js +93 -23
- package/dist/layout/edges.js.map +1 -1
- package/dist/layout/labels.d.ts +15 -4
- package/dist/layout/labels.d.ts.map +1 -1
- package/dist/layout/labels.js +404 -119
- package/dist/layout/labels.js.map +1 -1
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.d.ts.map +1 -1
- package/dist/manifest.js +3 -1
- package/dist/manifest.js.map +1 -1
- package/dist/render.d.ts +12 -2
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +2 -2
- package/dist/render.js.map +1 -1
- package/dist/svg/architecture.d.ts.map +1 -1
- package/dist/svg/architecture.js +3 -8
- package/dist/svg/architecture.js.map +1 -1
- package/dist/theme.d.ts +9 -8
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +61 -8
- package/dist/theme.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
- package/src/bounds.ts +29 -1
- package/src/design.ts +5 -0
- package/src/index.ts +2 -1
- package/src/layout/architecture.ts +7 -0
- package/src/layout/congestion.ts +71 -12
- package/src/layout/edges.ts +126 -21
- package/src/layout/labels.ts +495 -136
- package/src/manifest.ts +3 -2
- package/src/render.ts +19 -4
- package/src/svg/architecture.ts +3 -7
- package/src/theme.ts +62 -8
- package/src/version.ts +1 -1
package/src/manifest.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import type { GraphDoc, Lens, RenderAsset, RenderManifest } from "@coldtea/pr-lens-schema";
|
|
3
3
|
import { SCHEMA_VERSION } from "@coldtea/pr-lens-schema";
|
|
4
|
-
import type
|
|
4
|
+
import { THEMES, type Theme } from "./theme.js";
|
|
5
5
|
import { RENDERER_NAME, RENDERER_VERSION } from "./version.js";
|
|
6
6
|
|
|
7
7
|
/**
|
|
@@ -62,7 +62,8 @@ const escapeForFileName = (id: string): string =>
|
|
|
62
62
|
|
|
63
63
|
/** The contract's ceiling on an `Id`, which an asset id has to stay under. */
|
|
64
64
|
const ASSET_ID_MAX = 128;
|
|
65
|
-
|
|
65
|
+
/** The longest theme name, so a stem can never push an asset id over the cap. */
|
|
66
|
+
const THEME_SUFFIX_MAX = Math.max(...THEMES.map((theme) => theme.length)) + "-".length;
|
|
66
67
|
const STEM_MAX = ASSET_ID_MAX - THEME_SUFFIX_MAX;
|
|
67
68
|
const DIGEST_LENGTH = 12;
|
|
68
69
|
|
package/src/render.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { applyCorrections } from "./corrections.js";
|
|
|
5
5
|
import { PrLensRenderError } from "./errors.js";
|
|
6
6
|
import { buildManifest, contentHash, renderAssetFileName, renderAssetId } from "./manifest.js";
|
|
7
7
|
import { findView, flattenViews, resolveScope, type ScopedGraph } from "./scope.js";
|
|
8
|
-
import { paletteFor,
|
|
8
|
+
import { paletteFor, THEME_PAIR, type Theme } from "./theme.js";
|
|
9
9
|
import { paintArchitecture } from "./svg/architecture.js";
|
|
10
10
|
import { paintDataFlow } from "./svg/dataflow.js";
|
|
11
11
|
import { svgDocument } from "./svg/document.js";
|
|
@@ -122,10 +122,25 @@ const requireView = (views: readonly View[], id: string): View => {
|
|
|
122
122
|
return view;
|
|
123
123
|
};
|
|
124
124
|
|
|
125
|
+
/**
|
|
126
|
+
* What a render may be asked for: the `<picture>` pair, one half of it, or
|
|
127
|
+
* the single self-contained render.
|
|
128
|
+
*
|
|
129
|
+
* Not `readonly Theme[]`, which would admit all three at once — three assets
|
|
130
|
+
* per view against a view cap derived from two, so a document that validates
|
|
131
|
+
* could produce a manifest that does not. The contract's promise is that if
|
|
132
|
+
* it parses, it renders; this is the boundary that keeps it true.
|
|
133
|
+
*/
|
|
134
|
+
export type RenderThemes =
|
|
135
|
+
| readonly ["light", "dark"]
|
|
136
|
+
| readonly ["light"]
|
|
137
|
+
| readonly ["dark"]
|
|
138
|
+
| readonly ["neutral"];
|
|
139
|
+
|
|
125
140
|
export type RenderAllOptions = {
|
|
126
141
|
config?: Config;
|
|
127
|
-
/**
|
|
128
|
-
themes?:
|
|
142
|
+
/** The `<picture>` pair by default; `['neutral']` for a single-image surface. */
|
|
143
|
+
themes?: RenderThemes;
|
|
129
144
|
};
|
|
130
145
|
|
|
131
146
|
export type RenderedAsset = RenderedSvg & { asset: RenderAsset };
|
|
@@ -144,7 +159,7 @@ export type RenderAllResult = {
|
|
|
144
159
|
*/
|
|
145
160
|
export const renderAll = (doc: GraphDoc, options: RenderAllOptions = {}): RenderAllResult => {
|
|
146
161
|
const prepared = prepare(doc, options.config);
|
|
147
|
-
const themes = options.themes ??
|
|
162
|
+
const themes = options.themes ?? THEME_PAIR;
|
|
148
163
|
|
|
149
164
|
const targets: { lens: Lens; view: View | undefined }[] =
|
|
150
165
|
prepared.views.length > 0
|
package/src/svg/architecture.ts
CHANGED
|
@@ -8,7 +8,6 @@ import { travellingPulses } from "./pulse.js";
|
|
|
8
8
|
import type { ScopedGraph } from "../scope.js";
|
|
9
9
|
import { canvasFor, union } from "../bounds.js";
|
|
10
10
|
import { coord, type Box } from "../geometry.js";
|
|
11
|
-
import { placeLabelPills } from "../layout/labels.js";
|
|
12
11
|
import { relieveCongestion } from "../layout/congestion.js";
|
|
13
12
|
import { lines, tag, textNode, wrap } from "./primitives.js";
|
|
14
13
|
import { curveBounds, type RoutedEdge } from "../layout/edges.js";
|
|
@@ -28,6 +27,7 @@ import {
|
|
|
28
27
|
cardTextWidth,
|
|
29
28
|
deltaBadgeText,
|
|
30
29
|
laneHeaderText,
|
|
30
|
+
occupiedBoxes,
|
|
31
31
|
type PlacedNode,
|
|
32
32
|
} from "../layout/architecture.js";
|
|
33
33
|
import {
|
|
@@ -267,14 +267,10 @@ export const paintArchitecture = (
|
|
|
267
267
|
hints: LayoutHints | undefined,
|
|
268
268
|
palette: Palette,
|
|
269
269
|
): ArchitecturePainting => {
|
|
270
|
-
const { layout, routed } = relieveCongestion(graph, hints);
|
|
270
|
+
const { layout, routed, pills } = relieveCongestion(graph, hints);
|
|
271
271
|
const styles = stylesFor(palette);
|
|
272
|
-
const
|
|
272
|
+
const drawn: Box[] = occupiedBoxes(layout.nodes);
|
|
273
273
|
|
|
274
|
-
const drawn: Box[] = layout.nodes.flatMap((node) => {
|
|
275
|
-
const row = badgeRow(node);
|
|
276
|
-
return row === undefined ? [node.box] : [node.box, row.box];
|
|
277
|
-
});
|
|
278
274
|
const edgeMarkup: string[] = [];
|
|
279
275
|
const pillMarkup: string[] = [];
|
|
280
276
|
for (const edge of routed) {
|
package/src/theme.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { assertNever, THEMES, type Theme } from "@coldtea/pr-lens-schema";
|
|
1
|
+
import { assertNever, THEMES, THEME_PAIR, type Theme } from "@coldtea/pr-lens-schema";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Every theme a render can target, and the two that make a GitHub
|
|
5
|
+
* `<picture>` pair. A paired comment ships both halves and the client picks;
|
|
6
|
+
* no render may depend on the page it lands in, because an SVG served as an
|
|
7
|
+
* image cannot see it.
|
|
7
8
|
*
|
|
8
|
-
* The contract owns the pair, because
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* The contract owns the pair, because its size is one half of the arithmetic
|
|
10
|
+
* that bounds a document's view tree. Re-exported here so a caller rendering
|
|
11
|
+
* a diagram does not have to reach into the schema for it.
|
|
11
12
|
*/
|
|
12
|
-
export { THEMES, type Theme };
|
|
13
|
+
export { THEMES, THEME_PAIR, type Theme };
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Every colour the renderer can paint, resolved to a literal before it
|
|
@@ -106,12 +107,65 @@ const DARK: Palette = {
|
|
|
106
107
|
shadow: "rgba(0,0,0,.28)",
|
|
107
108
|
};
|
|
108
109
|
|
|
110
|
+
/**
|
|
111
|
+
* The one render for a surface that shows a single image and cannot swap on
|
|
112
|
+
* the reader's theme — GitLab and Bitbucket both.
|
|
113
|
+
*
|
|
114
|
+
* Handing such a surface the light asset is what it used to do, and it put a
|
|
115
|
+
* glaring white rectangle in front of every dark-mode reader, with strokes
|
|
116
|
+
* tuned for a light ground washing out against a dark page.
|
|
117
|
+
*
|
|
118
|
+
* A transparent render does not solve it either, and the arithmetic says why:
|
|
119
|
+
* for any flat colour, contrast against white is `1.05 / (L + 0.05)` and
|
|
120
|
+
* against black is `(L + 0.05) / 0.05`, which are equal only at L ≈ 0.179,
|
|
121
|
+
* where both are 4.58:1. That is the ceiling for text meant to sit directly
|
|
122
|
+
* on either page — barely over AA in the best case, and under it against a
|
|
123
|
+
* near-black like #0d1117.
|
|
124
|
+
*
|
|
125
|
+
* So this render is opaque and self-contained: it brings its own ground, and
|
|
126
|
+
* every contrast ratio inside it is fixed regardless of where it lands. The
|
|
127
|
+
* ground is dimmed rather than light — on a dark page it sits naturally, and
|
|
128
|
+
* on a light page it reads as a deliberately dark figure, the way a
|
|
129
|
+
* screenshot or a code block does. Its border is visible against both, so it
|
|
130
|
+
* never bleeds into the page.
|
|
131
|
+
*/
|
|
132
|
+
const NEUTRAL: Palette = {
|
|
133
|
+
background: "#22272e",
|
|
134
|
+
dot: "rgba(144,157,171,.2)",
|
|
135
|
+
lane: "rgba(144,157,171,.06)",
|
|
136
|
+
card: "#2d333b",
|
|
137
|
+
cardBorder: "#444c56",
|
|
138
|
+
foreground: "#cdd9e5",
|
|
139
|
+
muted: "#909dab",
|
|
140
|
+
edge: "#768390",
|
|
141
|
+
chip: "rgba(144,157,171,.16)",
|
|
142
|
+
pill: "#22272e",
|
|
143
|
+
pillBorder: "#373e47",
|
|
144
|
+
lifeline: "#444c56",
|
|
145
|
+
added: "#57ab5a",
|
|
146
|
+
addedText: "#6bc46d",
|
|
147
|
+
addedFill: "rgba(70,149,74,.18)",
|
|
148
|
+
addedBorder: "rgba(87,171,90,.45)",
|
|
149
|
+
modified: "#c69026",
|
|
150
|
+
modifiedText: "#daaa3f",
|
|
151
|
+
modifiedFill: "rgba(174,124,20,.18)",
|
|
152
|
+
modifiedBorder: "rgba(198,144,38,.45)",
|
|
153
|
+
removed: "#e5534b",
|
|
154
|
+
removedText: "#ff938a",
|
|
155
|
+
removedFill: "rgba(229,83,75,.14)",
|
|
156
|
+
removedBorder: "rgba(229,83,75,.45)",
|
|
157
|
+
neutralFill: "rgba(144,157,171,.16)",
|
|
158
|
+
shadow: "rgba(0,0,0,.3)",
|
|
159
|
+
};
|
|
160
|
+
|
|
109
161
|
export const paletteFor = (theme: Theme): Palette => {
|
|
110
162
|
switch (theme) {
|
|
111
163
|
case "light":
|
|
112
164
|
return LIGHT;
|
|
113
165
|
case "dark":
|
|
114
166
|
return DARK;
|
|
167
|
+
case "neutral":
|
|
168
|
+
return NEUTRAL;
|
|
115
169
|
default:
|
|
116
170
|
return assertNever(theme, "Unhandled theme");
|
|
117
171
|
}
|
package/src/version.ts
CHANGED