@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,15 @@
1
+ /**
2
+ * Every coordinate the renderer writes goes through here. Floating point
3
+ * arithmetic that differs in the last bit would otherwise change the bytes of
4
+ * the document without changing the picture, and the render hash with it.
5
+ */
6
+ export const coord = (value) => {
7
+ const rounded = Math.round(value * 100) / 100;
8
+ const normalised = Object.is(rounded, -0) ? 0 : rounded;
9
+ return String(normalised);
10
+ };
11
+ export const boxCentre = (box) => ({
12
+ x: box.x + box.width / 2,
13
+ y: box.y + box.height / 2,
14
+ });
15
+ //# sourceMappingURL=geometry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geometry.js","sourceRoot":"","sources":["../src/geometry.ts"],"names":[],"mappings":"AAOA;;;;GAIG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,KAAa,EAAU,EAAE;IAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC;IAC9C,MAAM,UAAU,GAAG,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IACxD,OAAO,MAAM,CAAC,UAAU,CAAC,CAAC;AAC5B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAQ,EAAS,EAAE,CAAC,CAAC;IAC7C,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,CAAC;IACxB,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC;CAC1B,CAAC,CAAC"}
package/dist/glob.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Path matching for config selectors, with the subset of glob a person
3
+ * actually writes in one: `**` crosses directory boundaries, `*` and `?` do
4
+ * not. Every other character is literal.
5
+ */
6
+ export declare const matchesGlob: (pattern: string, path: string) => boolean;
7
+ //# sourceMappingURL=glob.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../src/glob.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,eAAO,MAAM,WAAW,YAAa,MAAM,QAAQ,MAAM,KAAG,OAY3D,CAAC"}
package/dist/glob.js ADDED
@@ -0,0 +1,17 @@
1
+ const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2
+ /**
3
+ * Path matching for config selectors, with the subset of glob a person
4
+ * actually writes in one: `**` crosses directory boundaries, `*` and `?` do
5
+ * not. Every other character is literal.
6
+ */
7
+ export const matchesGlob = (pattern, path) => {
8
+ const source = pattern
9
+ .split("**")
10
+ .map((crossing) => crossing
11
+ .split("*")
12
+ .map((segment) => segment.split("?").map(escapeRegExp).join("[^/]"))
13
+ .join("[^/]*"))
14
+ .join(".*");
15
+ return new RegExp(`^${source}$`).test(path);
16
+ };
17
+ //# sourceMappingURL=glob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glob.js","sourceRoot":"","sources":["../src/glob.ts"],"names":[],"mappings":"AAAA,MAAM,YAAY,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAE7F;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,OAAe,EAAE,IAAY,EAAW,EAAE;IACpE,MAAM,MAAM,GAAG,OAAO;SACnB,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,QAAQ,EAAE,EAAE,CAChB,QAAQ;SACL,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;SACnE,IAAI,CAAC,OAAO,CAAC,CACjB;SACA,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO,IAAI,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { RENDERER_NAME, RENDERER_VERSION } from "./version.js";
2
+ export { render, renderAll, type RenderAllOptions, type RenderAllResult, type RenderedAsset, type RenderedSvg, type RenderOptions, } from "./render.js";
3
+ export { PrLensRenderError, type RenderErrorCode } from "./errors.js";
4
+ export { paletteFor, THEMES, type Palette, type Theme } from "./theme.js";
5
+ export { buildManifest, canonicalJson, contentHash, CONTENT_HASH_LENGTH, graphContentHash, renderAssetFileName, renderAssetId, type AssetAddress, } from "./manifest.js";
6
+ export { applyCorrections } from "./corrections.js";
7
+ export { findView, flattenViews, resolveScope, type ScopedGraph } from "./scope.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EACL,MAAM,EACN,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,aAAa,GACnB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAEtE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAE1E,OAAO,EACL,aAAa,EACb,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,KAAK,YAAY,GAClB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { RENDERER_NAME, RENDERER_VERSION } from "./version.js";
2
+ export { render, renderAll, } from "./render.js";
3
+ export { PrLensRenderError } from "./errors.js";
4
+ export { paletteFor, THEMES } from "./theme.js";
5
+ export { buildManifest, canonicalJson, contentHash, CONTENT_HASH_LENGTH, graphContentHash, renderAssetFileName, renderAssetId, } from "./manifest.js";
6
+ export { applyCorrections } from "./corrections.js";
7
+ export { findView, flattenViews, resolveScope } from "./scope.js";
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,EACL,MAAM,EACN,SAAS,GAMV,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,iBAAiB,EAAwB,MAAM,aAAa,CAAC;AAEtE,OAAO,EAAE,UAAU,EAAE,MAAM,EAA4B,MAAM,YAAY,CAAC;AAE1E,OAAO,EACL,aAAa,EACb,aAAa,EACb,WAAW,EACX,mBAAmB,EACnB,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,GAEd,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAoB,MAAM,YAAY,CAAC"}
@@ -0,0 +1,78 @@
1
+ import type { Delta, GraphNode, Lane, LayoutHints } from "@coldtea/pr-lens-schema";
2
+ import type { Box } from "../geometry.js";
3
+ import type { ScopedGraph } from "../scope.js";
4
+ export declare const deltaBadgeText: (delta: Delta) => string | undefined;
5
+ export declare const badgeWidth: (text: string) => number;
6
+ /** Every badge a card carries, its own first and the delta badge last. */
7
+ export declare const cardBadges: (node: GraphNode) => string[];
8
+ export type PlacedNode = {
9
+ node: GraphNode;
10
+ box: Box;
11
+ /** Wide enough for the kind glyph to earn its place. */
12
+ showIcon: boolean;
13
+ titleSize: number;
14
+ row: number;
15
+ laneIndex: number;
16
+ };
17
+ export type PlacedLane = {
18
+ lane: Lane;
19
+ box: Box;
20
+ };
21
+ /**
22
+ * The badges a card actually shows and the strip they occupy above it.
23
+ *
24
+ * When the row is wider than the card the badges the author added give way
25
+ * first: the delta badge is the one a reviewer is scanning for. Nothing is
26
+ * returned when the card carries no badge, and the strip counts as occupied
27
+ * space so an edge label does not settle on top of it.
28
+ */
29
+ export declare const badgeRow: (placed: PlacedNode) => {
30
+ badges: string[];
31
+ box: Box;
32
+ } | undefined;
33
+ /**
34
+ * The gaps of the grid, for the router: the vertical corridors beside each
35
+ * lane's cards and the horizontal extents of every row. Corridor `i` runs to
36
+ * the left of lane `i`; the extra corridor after the last lane is where
37
+ * retired pathways are exiled to.
38
+ */
39
+ export type LayoutGrid = {
40
+ rows: {
41
+ top: number;
42
+ height: number;
43
+ }[];
44
+ corridors: {
45
+ left: number;
46
+ right: number;
47
+ }[];
48
+ /** Where lane content ends — the floor of the last band under the last row. */
49
+ laneBottom: number;
50
+ /** First row of the dead band; absent when nothing was removed. */
51
+ deadFromRow: number | undefined;
52
+ };
53
+ /**
54
+ * Extra room granted to individual gaps, keyed by corridor or band index —
55
+ * how the layout widens where traffic would otherwise compress track pitch
56
+ * through the floor. Expanding a corridor moves every lane after it sideways
57
+ * by the same amount; expanding a band moves every row after it down. Cards
58
+ * travel with their lane and row — nothing re-seats, nothing reorders.
59
+ */
60
+ export type GapExpansions = {
61
+ corridors: ReadonlyMap<number, number>;
62
+ bands: ReadonlyMap<number, number>;
63
+ };
64
+ export type ArchitectureLayout = {
65
+ width: number;
66
+ height: number;
67
+ lanes: PlacedLane[];
68
+ nodes: PlacedNode[];
69
+ grid: LayoutGrid;
70
+ };
71
+ export declare const layoutArchitecture: (graph: ScopedGraph, hints: LayoutHints | undefined, expansions?: GapExpansions) => ArchitectureLayout;
72
+ /**
73
+ * The lane's own name over its band. Lanes never widen to fit their
74
+ * headers — that would put content in charge of where the next lane
75
+ * starts — so a header longer than the band gives up its tail instead.
76
+ */
77
+ export declare const laneHeaderText: (lane: Lane) => string;
78
+ //# sourceMappingURL=architecture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"architecture.d.ts","sourceRoot":"","sources":["../../src/layout/architecture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AA2BnF,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAI/C,eAAO,MAAM,cAAc,UAAW,KAAK,KAAG,MAAM,GAAG,SAatD,CAAC;AAUF,eAAO,MAAM,UAAU,SAAU,MAAM,KAAG,MAC8C,CAAC;AAEzF,0EAA0E;AAC1E,eAAO,MAAM,UAAU,SAAU,SAAS,KAAG,MAAM,EAGlD,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,GAAG,CAAC;IACT,wDAAwD;IACxD,QAAQ,EAAE,OAAO,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IAAE,IAAI,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,GAAG,CAAA;CAAE,CAAC;AAElD;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,WAAY,UAAU,KAAG;IAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,GAAG,EAAE,GAAG,CAAA;CAAE,GAAG,SAyB9E,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxC,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC7C,+EAA+E;IAC/E,UAAU,EAAE,MAAM,CAAC;IACnB,mEAAmE;IACnE,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACvC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,IAAI,EAAE,UAAU,CAAC;CAClB,CAAC;AA4CF,eAAO,MAAM,kBAAkB,UACtB,WAAW,SACX,WAAW,GAAG,SAAS,eACjB,aAAa,KACzB,kBA+FF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,cAAc,SAAU,IAAI,KAAG,MAM3C,CAAC"}
@@ -0,0 +1,207 @@
1
+ import { assertNever } from "@coldtea/pr-lens-schema";
2
+ import { BADGE_GAP, BADGE_HEIGHT, BADGE_PADDING_X, BADGE_RISE, BADGE_TEXT_SIZE, BADGE_TRACKING, CARD_GAP_X, CARD_HEIGHT, CARD_HEIGHT_WITH_SUBTITLE, CARD_PADDING_X, CONTENT_TOP, DIAGRAM_MARGIN, ICON_MIN_CARD_WIDTH, LANE_BOTTOM_PADDING, LANE_GAP, LANE_LABEL_SIZE, LANE_LABEL_TRACKING, LANE_CONTENT_WIDTH, LANE_PADDING_X, LANE_TOP, ROW_GAP, TITLE_SIZE, TITLE_SIZE_SMALL, } from "../design.js";
3
+ import { measure } from "../text.js";
4
+ import { seatNodes } from "./seating.js";
5
+ export const deltaBadgeText = (delta) => {
6
+ switch (delta) {
7
+ case "added":
8
+ return "NEW";
9
+ case "modified":
10
+ return "CHANGED";
11
+ case "removed":
12
+ return "REMOVED";
13
+ case "unchanged":
14
+ return undefined;
15
+ default:
16
+ return assertNever(delta, "Unhandled delta");
17
+ }
18
+ };
19
+ /** Width of tracked text: letter-spacing adds a fixed step after every glyph. */
20
+ const trackedWidth = (text, face, fontSize, tracking) => measure(text, face, fontSize) + [...text].length * tracking * fontSize;
21
+ export const badgeWidth = (text) => trackedWidth(text, "sans-bold", BADGE_TEXT_SIZE, BADGE_TRACKING) + BADGE_PADDING_X * 2;
22
+ /** Every badge a card carries, its own first and the delta badge last. */
23
+ export const cardBadges = (node) => {
24
+ const delta = deltaBadgeText(node.delta);
25
+ return delta === undefined ? [...node.badges] : [...node.badges, delta];
26
+ };
27
+ /**
28
+ * The badges a card actually shows and the strip they occupy above it.
29
+ *
30
+ * When the row is wider than the card the badges the author added give way
31
+ * first: the delta badge is the one a reviewer is scanning for. Nothing is
32
+ * returned when the card carries no badge, and the strip counts as occupied
33
+ * space so an edge label does not settle on top of it.
34
+ */
35
+ export const badgeRow = (placed) => {
36
+ const all = cardBadges(placed.node);
37
+ if (all.length === 0)
38
+ return undefined;
39
+ const room = placed.box.width - CARD_PADDING_X;
40
+ const shown = [];
41
+ let used = 0;
42
+ for (const text of [...all].reverse()) {
43
+ const next = used + badgeWidth(text) + (shown.length > 0 ? BADGE_GAP : 0);
44
+ if (next > room)
45
+ break;
46
+ used = next;
47
+ shown.unshift(text);
48
+ }
49
+ if (shown.length === 0)
50
+ return undefined;
51
+ const right = placed.box.x + placed.box.width - CARD_PADDING_X / 2;
52
+ return {
53
+ badges: shown,
54
+ box: {
55
+ x: right - used,
56
+ y: placed.box.y - BADGE_RISE,
57
+ width: used,
58
+ height: BADGE_HEIGHT,
59
+ },
60
+ };
61
+ };
62
+ const cardHeight = (node) => node.subtitle === undefined ? CARD_HEIGHT : CARD_HEIGHT_WITH_SUBTITLE;
63
+ /**
64
+ * Lane order: the document's explicit list first, for the lanes it names,
65
+ * then whatever is left by declared order and finally by the order they were
66
+ * written in. `order` may be absent, and two lanes may share one, so the
67
+ * document's own array is the tie-break that keeps this stable.
68
+ */
69
+ const orderLanes = (lanes, hints) => {
70
+ const named = hints?.laneOrder ?? [];
71
+ const byId = new Map(lanes.map((lane) => [lane.id, lane]));
72
+ const ordered = [];
73
+ const taken = new Set();
74
+ for (const id of named) {
75
+ const lane = byId.get(id);
76
+ if (lane === undefined || taken.has(id))
77
+ continue;
78
+ ordered.push(lane);
79
+ taken.add(id);
80
+ }
81
+ const rest = lanes
82
+ .map((lane, index) => ({ lane, index }))
83
+ .filter(({ lane }) => !taken.has(lane.id))
84
+ .sort((a, b) => (a.lane.order ?? Number.MAX_SAFE_INTEGER) - (b.lane.order ?? Number.MAX_SAFE_INTEGER) || a.index - b.index)
85
+ .map(({ lane }) => lane);
86
+ return [...ordered, ...rest];
87
+ };
88
+ /**
89
+ * A pair divides its row into equal halves. Splitting in proportion to what
90
+ * each card's text asked for would mean a rename moves its neighbour — and a
91
+ * rename moving anything is exactly what the seating guarantee rules out.
92
+ */
93
+ const rowWidths = (contentWidth, row) => {
94
+ if (row.nodes.length < 2)
95
+ return [contentWidth];
96
+ const half = Math.round((contentWidth - CARD_GAP_X) / 2);
97
+ return [half, contentWidth - CARD_GAP_X - half];
98
+ };
99
+ export const layoutArchitecture = (graph, hints, expansions) => {
100
+ const corridorExtra = (index) => expansions?.corridors.get(index) ?? 0;
101
+ const bandExtra = (index) => expansions?.bands.get(index) ?? 0;
102
+ const ordered = orderLanes(graph.lanes, hints);
103
+ const seating = seatNodes(ordered, graph.nodes, graph.edges, hints?.rank ?? {});
104
+ const lanes = ordered.flatMap((lane) => {
105
+ const rows = seating.rowsByLane.get(lane.id);
106
+ return rows === undefined ? [] : [{ lane, rows }];
107
+ });
108
+ const laneBoxWidth = LANE_CONTENT_WIDTH + LANE_PADDING_X * 2;
109
+ const gridHeights = new Map();
110
+ for (const { rows } of lanes)
111
+ for (const { grid, nodes } of rows) {
112
+ const height = Math.max(...nodes.map(cardHeight));
113
+ gridHeights.set(grid, Math.max(gridHeights.get(grid) ?? 0, height));
114
+ }
115
+ // Every row of the shared grid is occupied by whichever card created it, so
116
+ // walking 0..rowCount visits exactly the keys collected above.
117
+ const gridRows = [];
118
+ let cursor = CONTENT_TOP;
119
+ for (let grid = 0; grid < seating.rowCount; grid += 1) {
120
+ if (grid > 0)
121
+ cursor += bandExtra(grid);
122
+ const height = gridHeights.get(grid) ?? 0;
123
+ gridRows.push({ top: cursor, height });
124
+ cursor += height + ROW_GAP;
125
+ }
126
+ const contentBottom = cursor - ROW_GAP;
127
+ const laneBottom = contentBottom + LANE_BOTTOM_PADDING + bandExtra(seating.rowCount);
128
+ const placedLanes = [];
129
+ const placedNodes = [];
130
+ let laneX = DIAGRAM_MARGIN;
131
+ lanes.forEach(({ lane, rows }, laneIndex) => {
132
+ laneX += corridorExtra(laneIndex);
133
+ const contentX = laneX + LANE_PADDING_X;
134
+ const contentWidth = LANE_CONTENT_WIDTH;
135
+ placedLanes.push({
136
+ lane,
137
+ box: { x: laneX, y: LANE_TOP, width: laneBoxWidth, height: laneBottom - LANE_TOP },
138
+ });
139
+ for (const row of rows) {
140
+ const top = gridRows[row.grid]?.top ?? CONTENT_TOP;
141
+ const widths = rowWidths(contentWidth, row);
142
+ let x = contentX;
143
+ row.nodes.forEach((node, index) => {
144
+ const width = widths[index] ?? contentWidth;
145
+ placedNodes.push({
146
+ node,
147
+ box: { x, y: top, width, height: cardHeight(node) },
148
+ // Every kind draws something, on every card: the chip is reserved
149
+ // space and the title truncates, so recognition wins at any width.
150
+ showIcon: true,
151
+ titleSize: width >= ICON_MIN_CARD_WIDTH ? TITLE_SIZE : TITLE_SIZE_SMALL,
152
+ row: row.grid,
153
+ laneIndex,
154
+ });
155
+ x += width + CARD_GAP_X;
156
+ });
157
+ }
158
+ laneX += laneBoxWidth + LANE_GAP;
159
+ });
160
+ const gutter = LANE_PADDING_X * 2 + LANE_GAP;
161
+ const corridors = placedLanes.map(({ box }, index) => ({
162
+ left: box.x + LANE_PADDING_X - gutter - corridorExtra(index),
163
+ right: box.x + LANE_PADDING_X,
164
+ }));
165
+ const lastContentRight = (placedLanes[placedLanes.length - 1]?.box.x ?? DIAGRAM_MARGIN) +
166
+ LANE_PADDING_X +
167
+ LANE_CONTENT_WIDTH;
168
+ corridors.push({
169
+ left: lastContentRight,
170
+ right: lastContentRight + gutter + corridorExtra(placedLanes.length),
171
+ });
172
+ return {
173
+ // Whole numbers: the canvas is reported to the comment composer as pixels,
174
+ // and half a pixel of diagram is not a thing a reviewer can be shown.
175
+ width: Math.ceil(laneX - LANE_GAP + DIAGRAM_MARGIN),
176
+ height: Math.ceil(laneBottom + DIAGRAM_MARGIN),
177
+ lanes: placedLanes,
178
+ nodes: placedNodes,
179
+ grid: { rows: gridRows, corridors, laneBottom, deadFromRow: seating.deadFromRow },
180
+ };
181
+ };
182
+ /**
183
+ * The lane's own name over its band. Lanes never widen to fit their
184
+ * headers — that would put content in charge of where the next lane
185
+ * starts — so a header longer than the band gives up its tail instead.
186
+ */
187
+ export const laneHeaderText = (lane) => {
188
+ const full = (lane.subtitle === undefined ? lane.label : `${lane.label} · ${lane.subtitle}`).toUpperCase();
189
+ return truncateTracked(full, LANE_LABEL_SIZE, LANE_LABEL_TRACKING, LANE_CONTENT_WIDTH);
190
+ };
191
+ const ELLIPSIS = "…";
192
+ /** `truncate`, but counting the extra step letter-spacing puts after each glyph. */
193
+ const truncateTracked = (text, fontSize, tracking, maxWidth) => {
194
+ const width = (value) => trackedWidth(value, "sans-bold", fontSize, tracking);
195
+ if (width(text) <= maxWidth)
196
+ return text;
197
+ const characters = [...text];
198
+ let kept = 0;
199
+ while (kept < characters.length) {
200
+ const next = characters.slice(0, kept + 1).join("") + ELLIPSIS;
201
+ if (width(next) > maxWidth)
202
+ break;
203
+ kept += 1;
204
+ }
205
+ return kept === 0 ? ELLIPSIS : characters.slice(0, kept).join("").trimEnd() + ELLIPSIS;
206
+ };
207
+ //# sourceMappingURL=architecture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"architecture.js","sourceRoot":"","sources":["../../src/layout/architecture.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EACL,SAAS,EACT,YAAY,EACZ,eAAe,EACf,UAAU,EACV,eAAe,EACf,cAAc,EACd,UAAU,EACV,WAAW,EACX,yBAAyB,EACzB,cAAc,EACd,WAAW,EACX,cAAc,EACd,mBAAmB,EACnB,mBAAmB,EACnB,QAAQ,EACR,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,QAAQ,EACR,OAAO,EACP,UAAU,EACV,gBAAgB,GACjB,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,OAAO,EAAa,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,SAAS,EAAkB,MAAM,cAAc,CAAC;AAEzD,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,KAAY,EAAsB,EAAE;IACjE,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,OAAO;YACV,OAAO,KAAK,CAAC;QACf,KAAK,UAAU;YACb,OAAO,SAAS,CAAC;QACnB,KAAK,SAAS;YACZ,OAAO,SAAS,CAAC;QACnB,KAAK,WAAW;YACd,OAAO,SAAS,CAAC;QACnB;YACE,OAAO,WAAW,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;IACjD,CAAC;AACH,CAAC,CAAC;AAEF,iFAAiF;AACjF,MAAM,YAAY,GAAG,CACnB,IAAY,EACZ,IAAU,EACV,QAAgB,EAChB,QAAgB,EACR,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEpF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAY,EAAU,EAAE,CACjD,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,eAAe,EAAE,cAAc,CAAC,GAAG,eAAe,GAAG,CAAC,CAAC;AAEzF,0EAA0E;AAC1E,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAe,EAAY,EAAE;IACtD,MAAM,KAAK,GAAG,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzC,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;AAC1E,CAAC,CAAC;AAcF;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,MAAkB,EAA8C,EAAE;IACzF,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAEvC,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,GAAG,cAAc,CAAC;IAC/C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1E,IAAI,IAAI,GAAG,IAAI;YAAE,MAAM;QACvB,IAAI,GAAG,IAAI,CAAC;QACZ,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAEzC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,GAAG,cAAc,GAAG,CAAC,CAAC;IACnE,OAAO;QACL,MAAM,EAAE,KAAK;QACb,GAAG,EAAE;YACH,CAAC,EAAE,KAAK,GAAG,IAAI;YACf,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,UAAU;YAC5B,KAAK,EAAE,IAAI;YACX,MAAM,EAAE,YAAY;SACrB;KACF,CAAC;AACJ,CAAC,CAAC;AAqCF,MAAM,UAAU,GAAG,CAAC,IAAe,EAAU,EAAE,CAC7C,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,yBAAyB,CAAC;AAExE;;;;;GAKG;AACH,MAAM,UAAU,GAAG,CAAC,KAAsB,EAAE,KAA8B,EAAU,EAAE;IACpF,MAAM,KAAK,GAAG,KAAK,EAAE,SAAS,IAAI,EAAE,CAAC;IACrC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAW,EAAE,CAAC;IAC3B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAEhC,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC1B,IAAI,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,SAAS;QAClD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAChB,CAAC;IAED,MAAM,IAAI,GAAG,KAAK;SACf,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;SACvC,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;SACzC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,IAAI,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC;SAC1H,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IAE3B,OAAO,CAAC,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;AAC/B,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,SAAS,GAAG,CAAC,YAAoB,EAAE,GAAc,EAAY,EAAE;IACnE,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IAChD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IACzD,OAAO,CAAC,IAAI,EAAE,YAAY,GAAG,UAAU,GAAG,IAAI,CAAC,CAAC;AAClD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAChC,KAAkB,EAClB,KAA8B,EAC9B,UAA0B,EACN,EAAE;IACtB,MAAM,aAAa,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,UAAU,EAAE,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACvF,MAAM,SAAS,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,UAAU,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAE/E,MAAM,OAAO,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC/C,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,IAAI,EAAE,CAAC,CAAC;IAEhF,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;QACrC,MAAM,IAAI,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7C,OAAO,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,MAAM,YAAY,GAAG,kBAAkB,GAAG,cAAc,GAAG,CAAC,CAAC;IAE7D,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC9C,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,KAAK;QAC1B,KAAK,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,IAAI,EAAE,CAAC;YACnC,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;YAClD,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACtE,CAAC;IAEH,4EAA4E;IAC5E,+DAA+D;IAC/D,MAAM,QAAQ,GAAsC,EAAE,CAAC;IACvD,IAAI,MAAM,GAAG,WAAW,CAAC;IACzB,KAAK,IAAI,IAAI,GAAG,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,QAAQ,EAAE,IAAI,IAAI,CAAC,EAAE,CAAC;QACtD,IAAI,IAAI,GAAG,CAAC;YAAE,MAAM,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACvC,MAAM,IAAI,MAAM,GAAG,OAAO,CAAC;IAC7B,CAAC;IACD,MAAM,aAAa,GAAG,MAAM,GAAG,OAAO,CAAC;IAEvC,MAAM,UAAU,GAAG,aAAa,GAAG,mBAAmB,GAAG,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACrF,MAAM,WAAW,GAAiB,EAAE,CAAC;IACrC,MAAM,WAAW,GAAiB,EAAE,CAAC;IACrC,IAAI,KAAK,GAAG,cAAc,CAAC;IAE3B,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,EAAE;QAC1C,KAAK,IAAI,aAAa,CAAC,SAAS,CAAC,CAAC;QAClC,MAAM,QAAQ,GAAG,KAAK,GAAG,cAAc,CAAC;QACxC,MAAM,YAAY,GAAG,kBAAkB,CAAC;QAExC,WAAW,CAAC,IAAI,CAAC;YACf,IAAI;YACJ,GAAG,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,GAAG,QAAQ,EAAE;SACnF,CAAC,CAAC;QAEH,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,WAAW,CAAC;YACnD,MAAM,MAAM,GAAG,SAAS,CAAC,YAAY,EAAE,GAAG,CAAC,CAAC;YAE5C,IAAI,CAAC,GAAG,QAAQ,CAAC;YACjB,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;gBAChC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC;gBAC5C,WAAW,CAAC,IAAI,CAAC;oBACf,IAAI;oBACJ,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,IAAI,CAAC,EAAE;oBACnD,kEAAkE;oBAClE,mEAAmE;oBACnE,QAAQ,EAAE,IAAI;oBACd,SAAS,EAAE,KAAK,IAAI,mBAAmB,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,gBAAgB;oBACvE,GAAG,EAAE,GAAG,CAAC,IAAI;oBACb,SAAS;iBACV,CAAC,CAAC;gBACH,CAAC,IAAI,KAAK,GAAG,UAAU,CAAC;YAC1B,CAAC,CAAC,CAAC;QACL,CAAC;QAED,KAAK,IAAI,YAAY,GAAG,QAAQ,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,cAAc,GAAG,CAAC,GAAG,QAAQ,CAAC;IAC7C,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QACrD,IAAI,EAAE,GAAG,CAAC,CAAC,GAAG,cAAc,GAAG,MAAM,GAAG,aAAa,CAAC,KAAK,CAAC;QAC5D,KAAK,EAAE,GAAG,CAAC,CAAC,GAAG,cAAc;KAC9B,CAAC,CAAC,CAAC;IACJ,MAAM,gBAAgB,GACpB,CAAC,WAAW,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,cAAc,CAAC;QAC9D,cAAc;QACd,kBAAkB,CAAC;IACrB,SAAS,CAAC,IAAI,CAAC;QACb,IAAI,EAAE,gBAAgB;QACtB,KAAK,EAAE,gBAAgB,GAAG,MAAM,GAAG,aAAa,CAAC,WAAW,CAAC,MAAM,CAAC;KACrE,CAAC,CAAC;IAEH,OAAO;QACL,2EAA2E;QAC3E,sEAAsE;QACtE,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,QAAQ,GAAG,cAAc,CAAC;QACnD,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,cAAc,CAAC;QAC9C,KAAK,EAAE,WAAW;QAClB,KAAK,EAAE,WAAW;QAClB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,CAAC,WAAW,EAAE;KAClF,CAAC;AACJ,CAAC,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,IAAU,EAAU,EAAE;IACnD,MAAM,IAAI,GAAG,CACX,IAAI,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,MAAM,IAAI,CAAC,QAAQ,EAAE,CAC9E,CAAC,WAAW,EAAE,CAAC;IAEhB,OAAO,eAAe,CAAC,IAAI,EAAE,eAAe,EAAE,mBAAmB,EAAE,kBAAkB,CAAC,CAAC;AACzF,CAAC,CAAC;AAEF,MAAM,QAAQ,GAAG,GAAG,CAAC;AAErB,oFAAoF;AACpF,MAAM,eAAe,GAAG,CACtB,IAAY,EACZ,QAAgB,EAChB,QAAgB,EAChB,QAAgB,EACR,EAAE;IACV,MAAM,KAAK,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACtF,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IAEzC,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;IAC7B,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,OAAO,IAAI,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC;QAChC,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC;QAC/D,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,QAAQ;YAAE,MAAM;QAClC,IAAI,IAAI,CAAC,CAAC;IACZ,CAAC;IAED,OAAO,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,GAAG,QAAQ,CAAC;AACzF,CAAC,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { LayoutHints } from "@coldtea/pr-lens-schema";
2
+ import type { ScopedGraph } from "../scope.js";
3
+ import { type ArchitectureLayout } from "./architecture.js";
4
+ import { type RoutedEdge } from "./edges.js";
5
+ /**
6
+ * Layout and routing with the pitch floor held: any gap whose traffic would
7
+ * compress its tracks below TRACK_PITCH_MIN is widened to exactly what that
8
+ * traffic needs, and the graph is laid out again around the wider gap. The
9
+ * extra room is a pure function of the traffic count, so one added route
10
+ * moves the lanes or rows beside a saturated gap by one pitch step — a small
11
+ * change staying a small move — and an uncrowded document is laid out
12
+ * exactly as if this pass did not exist.
13
+ */
14
+ export declare const relieveCongestion: (graph: ScopedGraph, hints: LayoutHints | undefined) => {
15
+ layout: ArchitectureLayout;
16
+ routed: RoutedEdge[];
17
+ };
18
+ //# sourceMappingURL=congestion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"congestion.d.ts","sourceRoot":"","sources":["../../src/layout/congestion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAS3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAEL,KAAK,kBAAkB,EAGxB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAmD,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAE9F;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,UACrB,WAAW,SACX,WAAW,GAAG,SAAS,KAC7B;IAAE,MAAM,EAAE,kBAAkB,CAAC;IAAC,MAAM,EAAE,UAAU,EAAE,CAAA;CAepD,CAAC"}
@@ -0,0 +1,50 @@
1
+ import { LANE_BOTTOM_PADDING, LANE_GAP, LANE_PADDING_X, ROW_GAP, TRACK_CLEARANCE, TRACK_PITCH_MIN, } from "../design.js";
2
+ import { layoutArchitecture, } from "./architecture.js";
3
+ import { channelTraffic, routeEdges } from "./edges.js";
4
+ /**
5
+ * Layout and routing with the pitch floor held: any gap whose traffic would
6
+ * compress its tracks below TRACK_PITCH_MIN is widened to exactly what that
7
+ * traffic needs, and the graph is laid out again around the wider gap. The
8
+ * extra room is a pure function of the traffic count, so one added route
9
+ * moves the lanes or rows beside a saturated gap by one pitch step — a small
10
+ * change staying a small move — and an uncrowded document is laid out
11
+ * exactly as if this pass did not exist.
12
+ */
13
+ export const relieveCongestion = (graph, hints) => {
14
+ let expansions = { corridors: new Map(), bands: new Map() };
15
+ let layout = layoutArchitecture(graph, hints, expansions);
16
+ // Widening never changes which gaps the routes choose — plans are made of
17
+ // lane and row indices, and cards keep their in-lane positions — so the
18
+ // second round sees the same traffic and settles. The bound is a backstop.
19
+ for (let round = 0; round < 3; round += 1) {
20
+ const needed = expansionsFor(channelTraffic(graph.edges, layout), layout.grid);
21
+ if (sameExpansions(needed, expansions))
22
+ break;
23
+ expansions = needed;
24
+ layout = layoutArchitecture(graph, hints, expansions);
25
+ }
26
+ return { layout, routed: routeEdges(graph.edges, layout) };
27
+ };
28
+ /** Room for this many tracks at the floor pitch, plus clearance to the cards. */
29
+ const widthNeeded = (traffic) => (traffic - 1) * TRACK_PITCH_MIN + TRACK_CLEARANCE * 2;
30
+ const CORRIDOR_WIDTH = LANE_PADDING_X * 2 + LANE_GAP;
31
+ const expansionsFor = (traffic, grid) => {
32
+ const corridors = new Map();
33
+ for (const [index, count] of traffic.corridors) {
34
+ const extra = widthNeeded(count) - CORRIDOR_WIDTH;
35
+ if (extra > 0)
36
+ corridors.set(index, extra);
37
+ }
38
+ const bands = new Map();
39
+ for (const [index, count] of traffic.bands) {
40
+ // The band after the last row is the sliver of lane bottom padding.
41
+ const width = index === grid.rows.length ? LANE_BOTTOM_PADDING : ROW_GAP;
42
+ const extra = widthNeeded(count) - width;
43
+ if (extra > 0)
44
+ bands.set(index, extra);
45
+ }
46
+ return { corridors, bands };
47
+ };
48
+ const sameExpansions = (a, b) => sameEntries(a.corridors, b.corridors) && sameEntries(a.bands, b.bands);
49
+ const sameEntries = (a, b) => a.size === b.size && [...a].every(([key, value]) => b.get(key) === value);
50
+ //# sourceMappingURL=congestion.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"congestion.js","sourceRoot":"","sources":["../../src/layout/congestion.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,85 @@
1
+ import type { Flow, FlowMessage, GraphNode } from "@coldtea/pr-lens-schema";
2
+ import type { Box } from "../geometry.js";
3
+ export declare const PARTICIPANT_TOP = 18;
4
+ /** The lifeline hangs just clear of the card that heads its column. */
5
+ export declare const LIFELINE_GAP = 2;
6
+ /** Where the first message sits below the top of the lifelines. */
7
+ export declare const FIRST_MESSAGE_DROP = 41;
8
+ export declare const MESSAGE_PITCH = 38;
9
+ export declare const SELF_MESSAGE_PITCH = 54;
10
+ export declare const FLOW_BOTTOM_PADDING = 24;
11
+ export declare const FLOW_GAP = 40;
12
+ export declare const COLUMN_MIN_WIDTH = 150;
13
+ export declare const COLUMN_GAP = 80;
14
+ /**
15
+ * How far a column's band reaches beyond its cards, echoing the padding a
16
+ * lane keeps around the cards it holds.
17
+ */
18
+ export declare const FLOW_BAND_PAD_X = 12;
19
+ export declare const FLOW_BAND_PAD_Y = 12;
20
+ /** Half the width of an activation bar, and how far it holds arrows off the lifeline. */
21
+ export declare const ACTIVATION_HALF_WIDTH = 6;
22
+ export declare const MARKER_INSET = 5;
23
+ export declare const SELF_LOOP_REACH = 52;
24
+ export declare const SELF_LOOP_DROP = 6;
25
+ export declare const SELF_LOOP_CORNER = 7;
26
+ /** How far below its own row a self-message loop reaches. */
27
+ export declare const SELF_LOOP_EXTENT: number;
28
+ export type ActivationBar = {
29
+ top: number;
30
+ bottom: number;
31
+ };
32
+ export type PlacedParticipant = {
33
+ /** The node this column stands for, its label swapped for the column's. */
34
+ node: GraphNode;
35
+ centreX: number;
36
+ /** The node card heading the column, in the full card design system. */
37
+ card: Box;
38
+ /** Spans where this column is working on a synchronous call it received. */
39
+ activations: ActivationBar[];
40
+ };
41
+ export type PlacedMessage = {
42
+ message: FlowMessage;
43
+ /** The label as drawn, with a repeat count folded in when there is one. */
44
+ label: string;
45
+ y: number;
46
+ fromX: number;
47
+ toX: number;
48
+ /** Which beats of the drawing's cycle belong to this message, and how many. */
49
+ slot: {
50
+ start: number;
51
+ count: number;
52
+ };
53
+ };
54
+ export type FlowLayout = {
55
+ flow: Flow;
56
+ top: number;
57
+ height: number;
58
+ /** Where the lifelines start — under the tallest card in the header row. */
59
+ lifelineTop: number;
60
+ participants: PlacedParticipant[];
61
+ messages: PlacedMessage[];
62
+ };
63
+ export type DataFlowLayout = {
64
+ width: number;
65
+ height: number;
66
+ /** One width for every column of every flow, so stacked flows line up. */
67
+ columnWidth: number;
68
+ flows: FlowLayout[];
69
+ /** Beats in the shared cycle: one per pulse the drawing draws. */
70
+ slotCount: number;
71
+ };
72
+ /**
73
+ * How many turns a message takes in the cycle: one per repeat, up to the cap,
74
+ * so a repeated step crosses its own arrow several times running rather than
75
+ * once.
76
+ *
77
+ * A step the document leaves unanimated takes none. Reserving a turn for one
78
+ * that draws nothing would put a hole in the relay — the drawing would go
79
+ * dark for the length of it, which is the very thing sharing a cycle is meant
80
+ * to avoid. This is the one place that count is decided, so the schedule and
81
+ * the painting cannot disagree about it.
82
+ */
83
+ export declare const pulseCount: (message: FlowMessage, cap: number) => number;
84
+ export declare const layoutDataFlow: (flows: readonly Flow[], nodes: readonly GraphNode[], pulseCap: number) => DataFlowLayout;
85
+ //# sourceMappingURL=dataflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataflow.d.ts","sourceRoot":"","sources":["../../src/layout/dataflow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,WAAW,EAAmB,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAW7F,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAC;AAG1C,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,uEAAuE;AACvE,eAAO,MAAM,YAAY,IAAI,CAAC;AAC9B,mEAAmE;AACnE,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,aAAa,KAAK,CAAC;AAChC,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,mBAAmB,KAAK,CAAC;AACtC,eAAO,MAAM,QAAQ,KAAK,CAAC;AAE3B,eAAO,MAAM,gBAAgB,MAAM,CAAC;AACpC,eAAO,MAAM,UAAU,KAAK,CAAC;AAE7B;;;GAGG;AACH,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,eAAe,KAAK,CAAC;AAElC,yFAAyF;AACzF,eAAO,MAAM,qBAAqB,IAAI,CAAC;AACvC,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,eAAO,MAAM,cAAc,IAAI,CAAC;AAChC,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,6DAA6D;AAC7D,eAAO,MAAM,gBAAgB,QAAwC,CAAC;AAEtE,MAAM,MAAM,aAAa,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,2EAA2E;IAC3E,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,IAAI,EAAE,GAAG,CAAC;IACV,4EAA4E;IAC5E,WAAW,EAAE,aAAa,EAAE,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,WAAW,CAAC;IACrB,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,+EAA+E;IAC/E,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,IAAI,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,4EAA4E;IAC5E,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,iBAAiB,EAAE,CAAC;IAClC,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAKF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU,YAAa,WAAW,OAAO,MAAM,KAAG,MACJ,CAAC;AA4G5D,eAAO,MAAM,cAAc,UAClB,SAAS,IAAI,EAAE,SACf,SAAS,SAAS,EAAE,YACjB,MAAM,KACf,cA4FF,CAAC"}