@ng-prism/plugin-visual-regression 22.2.0-beta.1 → 22.2.0-beta.3

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 +1 @@
1
- {"version":3,"file":"visual-regression-panel.component.d.ts","sourceRoot":"","sources":["../src/visual-regression-panel.component.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,8BAA8B,CAAC;;AAEtC,qBA2La,8BAA8B;IACzC,QAAQ,CAAC,eAAe,+CAAwB;IAEhD,SAAS,CAAC,QAAQ,CAAC,WAAW,wDAA+B;IAE7D,SAAS,CAAC,QAAQ,CAAC,IAAI,0DASpB;IAEH,SAAS,CAAC,QAAQ,CAAC,YAAY,yCAE7B;IAEF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA+C;IAExE,SAAS,CAAC,QAAQ,CAAC,OAAO,0EAA8C;IAExE,SAAS,CAAC,QAAQ,CAAC,IAAI;;;;;;;SAYrB;IAEF,SAAS,CAAC,QAAQ,CAAC,QAAQ,0DAWxB;yCAjDQ,8BAA8B;2CAA9B,8BAA8B;CAkD1C"}
1
+ {"version":3,"file":"visual-regression-panel.component.d.ts","sourceRoot":"","sources":["../src/visual-regression-panel.component.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,8BAA8B,CAAC;;AAEtC,qBAqNa,8BAA8B;IACzC,QAAQ,CAAC,eAAe,+CAAwB;IAEhD,SAAS,CAAC,QAAQ,CAAC,WAAW,wDAA+B;IAE7D,SAAS,CAAC,QAAQ,CAAC,IAAI,0DASpB;IAEH,SAAS,CAAC,QAAQ,CAAC,YAAY,yCAE7B;IAEF,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA+C;IAExE,SAAS,CAAC,QAAQ,CAAC,OAAO,0EAA8C;IAExE,SAAS,CAAC,QAAQ,CAAC,IAAI;;;;;;;SAYrB;IAEF,SAAS,CAAC,QAAQ,CAAC,QAAQ,0DAWxB;yCAjDQ,8BAA8B;2CAA9B,8BAA8B;CAkD1C"}
@@ -42,21 +42,21 @@ function VisualRegressionPanelComponent_Conditional_0_Conditional_0_Conditional_
42
42
  i0.ɵɵelementEnd();
43
43
  } }
44
44
  function VisualRegressionPanelComponent_Conditional_0_Conditional_0_Template(rf, ctx) { if (rf & 1) {
45
- i0.ɵɵelementStart(0, "div", 1)(1, "div", 2);
46
- i0.ɵɵelement(2, "prism-vrt-summary", 3);
45
+ i0.ɵɵelementStart(0, "div", 1)(1, "div", 2)(2, "div", 3);
46
+ i0.ɵɵelement(3, "prism-vrt-summary", 4);
47
47
  i0.ɵɵelementEnd();
48
- i0.ɵɵelementStart(3, "div", 4)(4, "ul", 5);
48
+ i0.ɵɵelementStart(4, "ul", 5);
49
49
  i0.ɵɵrepeaterCreate(5, VisualRegressionPanelComponent_Conditional_0_Conditional_0_For_6_Template, 8, 6, "li", null, _forTrack0);
50
- i0.ɵɵelementEnd();
50
+ i0.ɵɵelementEnd()();
51
51
  i0.ɵɵelementStart(7, "div", 6);
52
52
  i0.ɵɵconditionalCreate(8, VisualRegressionPanelComponent_Conditional_0_Conditional_0_Conditional_8_Template, 1, 2, "prism-vrt-compare", 7)(9, VisualRegressionPanelComponent_Conditional_0_Conditional_0_Conditional_9_Template, 2, 0, "p", 0);
53
- i0.ɵɵelementEnd()()();
53
+ i0.ɵɵelementEnd()();
54
54
  } if (rf & 2) {
55
55
  let tmp_5_0;
56
56
  const ctx_r2 = i0.ɵɵnextContext(2);
57
- i0.ɵɵadvance(2);
58
- i0.ɵɵproperty("summary", ctx_r2.summary());
59
57
  i0.ɵɵadvance(3);
58
+ i0.ɵɵproperty("summary", ctx_r2.summary());
59
+ i0.ɵɵadvance(2);
60
60
  i0.ɵɵrepeater(ctx_r2.rows());
61
61
  i0.ɵɵadvance(3);
62
62
  i0.ɵɵconditional((tmp_5_0 = ctx_r2.selected()) ? 8 : 9, tmp_5_0);
@@ -126,23 +126,23 @@ export class VisualRegressionPanelComponent {
126
126
  }, /* @ts-ignore */
127
127
  ...(ngDevMode ? [{ debugName: "selected" }] : /* istanbul ignore next */ []));
128
128
  static ɵfac = function VisualRegressionPanelComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || VisualRegressionPanelComponent)(); };
129
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: VisualRegressionPanelComponent, selectors: [["prism-visual-regression-panel"]], inputs: { activeComponent: [1, "activeComponent"] }, decls: 2, vars: 1, consts: [[1, "vrt__empty"], [1, "vrt"], [1, "vrt__summary"], [3, "summary"], [1, "vrt__split"], [1, "vrt__list"], [1, "vrt__viewer"], [3, "variant", "assetBaseUrl"], ["type", "button", 1, "vrt__row", 3, "click"], [1, "vrt__name"], [1, "vrt__status"], [1, "vrt__diff"]], template: function VisualRegressionPanelComponent_Template(rf, ctx) { if (rf & 1) {
129
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: VisualRegressionPanelComponent, selectors: [["prism-visual-regression-panel"]], inputs: { activeComponent: [1, "activeComponent"] }, decls: 2, vars: 1, consts: [[1, "vrt__empty"], [1, "vrt"], [1, "vrt__aside"], [1, "vrt__summary"], [3, "summary"], [1, "vrt__list"], [1, "vrt__viewer"], [3, "variant", "assetBaseUrl"], ["type", "button", 1, "vrt__row", 3, "click"], [1, "vrt__name"], [1, "vrt__status"], [1, "vrt__diff"]], template: function VisualRegressionPanelComponent_Template(rf, ctx) { if (rf & 1) {
130
130
  i0.ɵɵconditionalCreate(0, VisualRegressionPanelComponent_Conditional_0_Template, 2, 1)(1, VisualRegressionPanelComponent_Conditional_1_Template, 8, 0, "div", 0);
131
131
  } if (rf & 2) {
132
132
  let tmp_0_0;
133
133
  i0.ɵɵconditional((tmp_0_0 = ctx.meta()) ? 0 : 1, tmp_0_0);
134
- } }, dependencies: [VrtCompareComponent, VrtSummaryComponent], styles: ["[_nghost-%COMP%] {\n display: block;\n height: 100%;\n overflow: auto;\n font-size: var(--fs-md);\n color: var(--prism-text);\n }\n\n .vrt[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n min-height: 100%;\n }\n\n .vrt__summary[_ngcontent-%COMP%] { padding: 14px 16px 0; }\n\n .vrt__split[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: minmax(200px, 250px) 1fr;\n flex: 1;\n min-height: 0;\n padding-top: 14px;\n }\n\n .vrt__list[_ngcontent-%COMP%] {\n margin: 0;\n padding: 0 4px 14px 16px;\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n }\n\n \n\n .vrt__row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 1fr auto;\n grid-template-areas: 'name diff' 'status diff';\n align-items: center;\n gap: 0 10px;\n width: 100%;\n padding: 8px 11px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-left: 3px solid var(--tone-color, var(--prism-border-strong));\n border-radius: var(--radius-md);\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n transition: border-color var(--dur-fast), background var(--dur-fast);\n }\n .vrt__row[_ngcontent-%COMP%]:hover { border-color: var(--prism-border-strong); }\n .vrt__row[_ngcontent-%COMP%]:hover { border-left-color: var(--tone-color, var(--prism-border-strong)); }\n .vrt__row--active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--prism-primary) 10%, var(--prism-bg-surface));\n border-color: color-mix(in srgb, var(--prism-primary) 40%, transparent);\n border-left-color: var(--tone-color, var(--prism-primary));\n }\n\n .vrt__name[_ngcontent-%COMP%] {\n grid-area: name;\n font-weight: 600;\n font-size: var(--fs-md);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .vrt__status[_ngcontent-%COMP%] {\n grid-area: status;\n justify-self: start;\n margin-top: 3px;\n font-family: var(--font-mono);\n font-size: 9.5px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n padding: 2px 6px;\n border-radius: var(--radius-xs);\n background: color-mix(in srgb, var(--tone-color) 15%, transparent);\n color: var(--tone-color);\n }\n\n .vrt__diff[_ngcontent-%COMP%] {\n grid-area: diff;\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n white-space: nowrap;\n }\n\n [data-tone='success'][_ngcontent-%COMP%] { --tone-color: var(--prism-success); }\n [data-tone='danger'][_ngcontent-%COMP%] { --tone-color: var(--prism-danger); }\n [data-tone='warn'][_ngcontent-%COMP%] { --tone-color: var(--prism-warn); }\n [data-tone='neutral'][_ngcontent-%COMP%] { --tone-color: var(--prism-primary); }\n [data-tone='muted'][_ngcontent-%COMP%] { --tone-color: var(--prism-text-muted); }\n\n .vrt__viewer[_ngcontent-%COMP%] {\n min-width: 0;\n min-height: 0;\n border-left: 1px solid var(--prism-border);\n }\n\n .vrt__empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n min-height: 140px;\n padding: 24px;\n text-align: center;\n font-size: var(--fs-md);\n }\n .vrt__empty[_ngcontent-%COMP%] b[_ngcontent-%COMP%] { font-weight: 600; color: var(--prism-text-2); }\n .vrt__empty[_ngcontent-%COMP%] span[_ngcontent-%COMP%] { color: var(--prism-text-muted); }\n .vrt__empty[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n background: var(--prism-input-bg);\n }\n\n @media (max-width: 860px) {\n .vrt__split[_ngcontent-%COMP%] { grid-template-columns: 1fr; }\n .vrt__list[_ngcontent-%COMP%] { padding-right: 16px; }\n .vrt__viewer[_ngcontent-%COMP%] { border-left: none; border-top: 1px solid var(--prism-border); }\n }"] });
134
+ } }, dependencies: [VrtCompareComponent, VrtSummaryComponent], styles: ["\n\n\n\n\n\n\n\n\n\n\n [_nghost-%COMP%] {\n display: block;\n height: 100%;\n overflow: hidden;\n font-size: var(--fs-md);\n color: var(--prism-text);\n }\n\n .vrt[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: minmax(200px, 250px) 1fr;\n height: 100%;\n min-height: 0;\n }\n\n .vrt__aside[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n min-height: 0;\n min-width: 0;\n }\n\n .vrt__summary[_ngcontent-%COMP%] {\n flex: none;\n padding: 12px 12px 11px 16px;\n border-bottom: 1px solid var(--prism-border);\n }\n\n .vrt__list[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n overflow: auto;\n margin: 0;\n padding: 12px 4px 12px 16px;\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n\n \n\n .vrt__row[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 1fr auto;\n grid-template-areas: 'name diff' 'status diff';\n align-items: center;\n gap: 0 10px;\n width: 100%;\n padding: 8px 11px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-left: 3px solid var(--tone-color, var(--prism-border-strong));\n border-radius: var(--radius-md);\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n transition: border-color var(--dur-fast), background var(--dur-fast);\n }\n .vrt__row[_ngcontent-%COMP%]:hover { border-color: var(--prism-border-strong); }\n .vrt__row[_ngcontent-%COMP%]:hover { border-left-color: var(--tone-color, var(--prism-border-strong)); }\n .vrt__row--active[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--prism-primary) 10%, var(--prism-bg-surface));\n border-color: color-mix(in srgb, var(--prism-primary) 40%, transparent);\n border-left-color: var(--tone-color, var(--prism-primary));\n }\n\n .vrt__name[_ngcontent-%COMP%] {\n grid-area: name;\n font-weight: 600;\n font-size: var(--fs-md);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .vrt__status[_ngcontent-%COMP%] {\n grid-area: status;\n justify-self: start;\n margin-top: 3px;\n font-family: var(--font-mono);\n font-size: 9.5px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n padding: 2px 6px;\n border-radius: var(--radius-xs);\n background: color-mix(in srgb, var(--tone-color) 15%, transparent);\n color: var(--tone-color);\n }\n\n .vrt__diff[_ngcontent-%COMP%] {\n grid-area: diff;\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n white-space: nowrap;\n }\n\n [data-tone='success'][_ngcontent-%COMP%] { --tone-color: var(--prism-success); }\n [data-tone='danger'][_ngcontent-%COMP%] { --tone-color: var(--prism-danger); }\n [data-tone='warn'][_ngcontent-%COMP%] { --tone-color: var(--prism-warn); }\n [data-tone='neutral'][_ngcontent-%COMP%] { --tone-color: var(--prism-primary); }\n [data-tone='muted'][_ngcontent-%COMP%] { --tone-color: var(--prism-text-muted); }\n\n .vrt__viewer[_ngcontent-%COMP%] {\n min-width: 0;\n min-height: 0;\n overflow: auto;\n border-left: 1px solid var(--prism-border);\n }\n\n .vrt__empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n min-height: 140px;\n padding: 24px;\n text-align: center;\n font-size: var(--fs-md);\n }\n .vrt__empty[_ngcontent-%COMP%] b[_ngcontent-%COMP%] { font-weight: 600; color: var(--prism-text-2); }\n .vrt__empty[_ngcontent-%COMP%] span[_ngcontent-%COMP%] { color: var(--prism-text-muted); }\n .vrt__empty[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n background: var(--prism-input-bg);\n }\n\n \n\n\n @media (max-width: 860px) {\n [_nghost-%COMP%] { overflow: auto; }\n .vrt[_ngcontent-%COMP%] { grid-template-columns: 1fr; height: auto; }\n .vrt__list[_ngcontent-%COMP%] { overflow: visible; padding-right: 16px; }\n .vrt__viewer[_ngcontent-%COMP%] {\n overflow: visible;\n border-left: none;\n border-top: 1px solid var(--prism-border);\n }\n }"] });
135
135
  }
136
136
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(VisualRegressionPanelComponent, [{
137
137
  type: Component,
138
138
  args: [{ selector: 'prism-visual-regression-panel', standalone: true, imports: [VrtCompareComponent, VrtSummaryComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
139
139
  @if (meta(); as m) { @if (m.found) {
140
140
  <div class="vrt">
141
- <div class="vrt__summary">
142
- <prism-vrt-summary [summary]="summary()" />
143
- </div>
141
+ <div class="vrt__aside">
142
+ <div class="vrt__summary">
143
+ <prism-vrt-summary [summary]="summary()" />
144
+ </div>
144
145
 
145
- <div class="vrt__split">
146
146
  <ul class="vrt__list">
147
147
  @for (row of rows(); track row.key) {
148
148
  <li>
@@ -160,14 +160,14 @@ export class VisualRegressionPanelComponent {
160
160
  </li>
161
161
  }
162
162
  </ul>
163
+ </div>
163
164
 
164
- <div class="vrt__viewer">
165
- @if (selected(); as v) {
166
- <prism-vrt-compare [variant]="v" [assetBaseUrl]="assetBaseUrl()" />
167
- } @else {
168
- <p class="vrt__empty">Select a variant.</p>
169
- }
170
- </div>
165
+ <div class="vrt__viewer">
166
+ @if (selected(); as v) {
167
+ <prism-vrt-compare [variant]="v" [assetBaseUrl]="assetBaseUrl()" />
168
+ } @else {
169
+ <p class="vrt__empty">Select a variant.</p>
170
+ }
171
171
  </div>
172
172
  </div>
173
173
  } @else {
@@ -181,9 +181,9 @@ export class VisualRegressionPanelComponent {
181
181
  <span>Check the plugin's <code>reportPath</code>.</span>
182
182
  </div>
183
183
  }
184
- `, styles: ["\n :host {\n display: block;\n height: 100%;\n overflow: auto;\n font-size: var(--fs-md);\n color: var(--prism-text);\n }\n\n .vrt {\n display: flex;\n flex-direction: column;\n min-height: 100%;\n }\n\n .vrt__summary { padding: 14px 16px 0; }\n\n .vrt__split {\n display: grid;\n grid-template-columns: minmax(200px, 250px) 1fr;\n flex: 1;\n min-height: 0;\n padding-top: 14px;\n }\n\n .vrt__list {\n margin: 0;\n padding: 0 4px 14px 16px;\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 0;\n }\n\n /* Same card-with-severity-edge as the a11y violations list, so the two\n panels read as one product. */\n .vrt__row {\n display: grid;\n grid-template-columns: 1fr auto;\n grid-template-areas: 'name diff' 'status diff';\n align-items: center;\n gap: 0 10px;\n width: 100%;\n padding: 8px 11px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-left: 3px solid var(--tone-color, var(--prism-border-strong));\n border-radius: var(--radius-md);\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n transition: border-color var(--dur-fast), background var(--dur-fast);\n }\n .vrt__row:hover { border-color: var(--prism-border-strong); }\n .vrt__row:hover { border-left-color: var(--tone-color, var(--prism-border-strong)); }\n .vrt__row--active {\n background: color-mix(in srgb, var(--prism-primary) 10%, var(--prism-bg-surface));\n border-color: color-mix(in srgb, var(--prism-primary) 40%, transparent);\n border-left-color: var(--tone-color, var(--prism-primary));\n }\n\n .vrt__name {\n grid-area: name;\n font-weight: 600;\n font-size: var(--fs-md);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .vrt__status {\n grid-area: status;\n justify-self: start;\n margin-top: 3px;\n font-family: var(--font-mono);\n font-size: 9.5px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n padding: 2px 6px;\n border-radius: var(--radius-xs);\n background: color-mix(in srgb, var(--tone-color) 15%, transparent);\n color: var(--tone-color);\n }\n\n .vrt__diff {\n grid-area: diff;\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n white-space: nowrap;\n }\n\n [data-tone='success'] { --tone-color: var(--prism-success); }\n [data-tone='danger'] { --tone-color: var(--prism-danger); }\n [data-tone='warn'] { --tone-color: var(--prism-warn); }\n [data-tone='neutral'] { --tone-color: var(--prism-primary); }\n [data-tone='muted'] { --tone-color: var(--prism-text-muted); }\n\n .vrt__viewer {\n min-width: 0;\n min-height: 0;\n border-left: 1px solid var(--prism-border);\n }\n\n .vrt__empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n min-height: 140px;\n padding: 24px;\n text-align: center;\n font-size: var(--fs-md);\n }\n .vrt__empty b { font-weight: 600; color: var(--prism-text-2); }\n .vrt__empty span { color: var(--prism-text-muted); }\n .vrt__empty code {\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n background: var(--prism-input-bg);\n }\n\n @media (max-width: 860px) {\n .vrt__split { grid-template-columns: 1fr; }\n .vrt__list { padding-right: 16px; }\n .vrt__viewer { border-left: none; border-top: 1px solid var(--prism-border); }\n }\n "] }]
184
+ `, styles: ["\n /*\n * Not a scroll container.\n *\n * It used to be overflow:auto, which made the whole panel one scroller:\n * both columns simply grew and the panel scrolled as a unit, so picking a\n * variant further down the list scrolled the comparison off the top and\n * you had to scroll back up to see what you had picked. The two columns\n * own their scrolling now, and the chain of min-height:0 below is what\n * lets them \u2014 a grid or flex item defaults to min-height:auto, refuses\n * to shrink below its content, and pushes the overflow back up here.\n */\n :host {\n display: block;\n height: 100%;\n overflow: hidden;\n font-size: var(--fs-md);\n color: var(--prism-text);\n }\n\n .vrt {\n display: grid;\n grid-template-columns: minmax(200px, 250px) 1fr;\n height: 100%;\n min-height: 0;\n }\n\n .vrt__aside {\n display: flex;\n flex-direction: column;\n min-height: 0;\n min-width: 0;\n }\n\n .vrt__summary {\n flex: none;\n padding: 12px 12px 11px 16px;\n border-bottom: 1px solid var(--prism-border);\n }\n\n .vrt__list {\n flex: 1;\n min-height: 0;\n overflow: auto;\n margin: 0;\n padding: 12px 4px 12px 16px;\n list-style: none;\n display: flex;\n flex-direction: column;\n gap: 6px;\n }\n\n /* Same card-with-severity-edge as the a11y violations list, so the two\n panels read as one product. */\n .vrt__row {\n display: grid;\n grid-template-columns: 1fr auto;\n grid-template-areas: 'name diff' 'status diff';\n align-items: center;\n gap: 0 10px;\n width: 100%;\n padding: 8px 11px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-left: 3px solid var(--tone-color, var(--prism-border-strong));\n border-radius: var(--radius-md);\n color: inherit;\n font: inherit;\n text-align: left;\n cursor: pointer;\n transition: border-color var(--dur-fast), background var(--dur-fast);\n }\n .vrt__row:hover { border-color: var(--prism-border-strong); }\n .vrt__row:hover { border-left-color: var(--tone-color, var(--prism-border-strong)); }\n .vrt__row--active {\n background: color-mix(in srgb, var(--prism-primary) 10%, var(--prism-bg-surface));\n border-color: color-mix(in srgb, var(--prism-primary) 40%, transparent);\n border-left-color: var(--tone-color, var(--prism-primary));\n }\n\n .vrt__name {\n grid-area: name;\n font-weight: 600;\n font-size: var(--fs-md);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n .vrt__status {\n grid-area: status;\n justify-self: start;\n margin-top: 3px;\n font-family: var(--font-mono);\n font-size: 9.5px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n padding: 2px 6px;\n border-radius: var(--radius-xs);\n background: color-mix(in srgb, var(--tone-color) 15%, transparent);\n color: var(--tone-color);\n }\n\n .vrt__diff {\n grid-area: diff;\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n white-space: nowrap;\n }\n\n [data-tone='success'] { --tone-color: var(--prism-success); }\n [data-tone='danger'] { --tone-color: var(--prism-danger); }\n [data-tone='warn'] { --tone-color: var(--prism-warn); }\n [data-tone='neutral'] { --tone-color: var(--prism-primary); }\n [data-tone='muted'] { --tone-color: var(--prism-text-muted); }\n\n .vrt__viewer {\n min-width: 0;\n min-height: 0;\n overflow: auto;\n border-left: 1px solid var(--prism-border);\n }\n\n .vrt__empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 4px;\n min-height: 140px;\n padding: 24px;\n text-align: center;\n font-size: var(--fs-md);\n }\n .vrt__empty b { font-weight: 600; color: var(--prism-text-2); }\n .vrt__empty span { color: var(--prism-text-muted); }\n .vrt__empty code {\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n background: var(--prism-input-bg);\n }\n\n /* Too narrow for two columns: stack them, and hand the scrolling back to\n the panel as a whole \u2014 side by side the two scrollers keep the list and\n the image in view at once, stacked there is nothing to keep in view. */\n @media (max-width: 860px) {\n :host { overflow: auto; }\n .vrt { grid-template-columns: 1fr; height: auto; }\n .vrt__list { overflow: visible; padding-right: 16px; }\n .vrt__viewer {\n overflow: visible;\n border-left: none;\n border-top: 1px solid var(--prism-border);\n }\n }\n "] }]
185
185
  }], null, { activeComponent: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeComponent", required: false }] }] }); })();
186
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(VisualRegressionPanelComponent, { className: "VisualRegressionPanelComponent", filePath: "visual-regression-panel.component.ts", lineNumber: 208 }); })();
186
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(VisualRegressionPanelComponent, { className: "VisualRegressionPanelComponent", filePath: "visual-regression-panel.component.ts", lineNumber: 234 }); })();
187
187
  function formatDiff(variant) {
188
188
  if (variant.diffRatio === undefined)
189
189
  return '—';
package/dist/vrt-bg.d.ts CHANGED
@@ -3,18 +3,20 @@ import type { VrtVariantResult } from './visual-regression.types.js';
3
3
  /**
4
4
  * Background declarations for the box that holds a capture.
5
5
  *
6
- * The captures themselves are opaque, so this is mostly invisible except
7
- * under a diff mask, where the unchanged pixels a comparator leaves
8
- * transparent let it through. There the surface decides whether a diff reads
9
- * as a diff or as a chequered mess.
6
+ * Mostly invisible behind an opaque capture but not behind every one. It
7
+ * shows through a diff mask, where the unchanged pixels a comparator leaves
8
+ * transparent let it past, and through a `transparent` capture, which is see-
9
+ * through by design. In both places the surface decides whether the image
10
+ * reads as itself or as a chequered mess.
10
11
  *
11
- * Only `light` and `dark` produce a colour. `dots`, `plain` and `checker` all
12
- * paint `--prism-bg-surface`, a *theme* token, and the panel cannot know which
13
- * theme the runner's browser ran in so those keep the checkerboard, which
14
- * reads as "transparent" rather than as a guess painted as fact. For `checker`
15
- * that is also the literal rendering, and since it is what an undeclared
16
- * variant resolves to, an undeclared capture frames itself correctly. Same for
17
- * a report that recorded no background at all.
12
+ * Only `light` and `dark` produce a colour. The rest keep the checkerboard,
13
+ * for two different reasons. `dots`, `plain` and `checker` paint
14
+ * `--prism-bg-surface`, a *theme* token, and the panel cannot know which theme
15
+ * the runner's browser ran in so a colour here would be a guess painted as
16
+ * fact. `transparent` is the opposite case: its capture genuinely has an alpha
17
+ * channel, and the checkerboard is what makes that legible instead of
18
+ * inventing a surface the capture deliberately omitted. A report that recorded
19
+ * no background at all is treated the same way.
18
20
  */
19
21
  export declare function shotSurfaceStyle(bg: CanvasBg | undefined): Record<string, string>;
20
22
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"vrt-bg.d.ts","sourceRoot":"","sources":["../src/vrt-bg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAerE;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,QAAQ,GAAG,SAAS,GACvB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,gBAAgB,GACxB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,QAAQ,CAAA;CAAE,GAAG,IAAI,CAIzC"}
1
+ {"version":3,"file":"vrt-bg.d.ts","sourceRoot":"","sources":["../src/vrt-bg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAerE;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,QAAQ,GAAG,SAAS,GACvB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIxB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,QAAQ,CACtB,OAAO,EAAE,gBAAgB,GACxB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,QAAQ,CAAA;CAAE,GAAG,IAAI,CAIzC"}
package/dist/vrt-bg.js CHANGED
@@ -13,18 +13,20 @@ const SURFACE = {
13
13
  /**
14
14
  * Background declarations for the box that holds a capture.
15
15
  *
16
- * The captures themselves are opaque, so this is mostly invisible except
17
- * under a diff mask, where the unchanged pixels a comparator leaves
18
- * transparent let it through. There the surface decides whether a diff reads
19
- * as a diff or as a chequered mess.
16
+ * Mostly invisible behind an opaque capture but not behind every one. It
17
+ * shows through a diff mask, where the unchanged pixels a comparator leaves
18
+ * transparent let it past, and through a `transparent` capture, which is see-
19
+ * through by design. In both places the surface decides whether the image
20
+ * reads as itself or as a chequered mess.
20
21
  *
21
- * Only `light` and `dark` produce a colour. `dots`, `plain` and `checker` all
22
- * paint `--prism-bg-surface`, a *theme* token, and the panel cannot know which
23
- * theme the runner's browser ran in so those keep the checkerboard, which
24
- * reads as "transparent" rather than as a guess painted as fact. For `checker`
25
- * that is also the literal rendering, and since it is what an undeclared
26
- * variant resolves to, an undeclared capture frames itself correctly. Same for
27
- * a report that recorded no background at all.
22
+ * Only `light` and `dark` produce a colour. The rest keep the checkerboard,
23
+ * for two different reasons. `dots`, `plain` and `checker` paint
24
+ * `--prism-bg-surface`, a *theme* token, and the panel cannot know which theme
25
+ * the runner's browser ran in so a colour here would be a guess painted as
26
+ * fact. `transparent` is the opposite case: its capture genuinely has an alpha
27
+ * channel, and the checkerboard is what makes that legible instead of
28
+ * inventing a surface the capture deliberately omitted. A report that recorded
29
+ * no background at all is treated the same way.
28
30
  */
29
31
  export function shotSurfaceStyle(bg) {
30
32
  const surface = bg ? SURFACE[bg] : undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"vrt-compare.component.d.ts","sourceRoot":"","sources":["../src/vrt-compare.component.ts"],"names":[],"mappings":"AAUA,OAAO,EAA0B,KAAK,IAAI,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;;AAErE,KAAK,WAAW,GAAG,MAAM,GAAG,cAAc,GAAG,MAAM,CAAC;AAQpD;;;;;GAKG;AACH,qBAkZa,mBAAmB;IAC9B,QAAQ,CAAC,OAAO,wDAAsC;IACtD,QAAQ,CAAC,YAAY,8CAAa;IAElC,SAAS,CAAC,QAAQ,CAAC,aAAa,6DAAoC;IACpE,SAAS,CAAC,QAAQ,CAAC,IAAI,+CAAuB;IAC9C,SAAS,CAAC,QAAQ,CAAC,IAAI,iDAAc;IAErC,SAAS,CAAC,QAAQ,CAAC,UAAU,8BAAc;IAC3C,SAAS,CAAC,QAAQ,CAAC,KAAK,kBAAS;IAEjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAE1B;IACF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAEzB;IACF,SAAS,CAAC,QAAQ,CAAC,OAAO,qDAExB;IAEF;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,MAAM;;;;;cAUtB;IAEH;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,cAAc,yDAE/B;IAEF,SAAS,CAAC,QAAQ,CAAC,eAAe,yDAEhC;IAEF,6EAA6E;IAC7E,SAAS,CAAC,QAAQ,CAAC,MAAM;;;cAA4C;IAErE,SAAS,CAAC,QAAQ,CAAC,WAAW;;;;;;cAe3B;IAEH,SAAS,CAAC,QAAQ,CAAC,cAAc,gDAgB9B;IAEH,SAAS,CAAC,QAAQ,CAAC,UAAU,8CAM1B;IAEH,yEAAyE;IACzE,SAAS,CAAC,QAAQ,CAAC,OAAO,0CAExB;IAEF;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,WAAW,0CAQ3B;IAEH,qEAAqE;IACrE,SAAS,CAAC,QAAQ,CAAC,UAAU,yDAG1B;IAEH,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;yCA5H7B,mBAAmB;2CAAnB,mBAAmB;CA+H/B"}
1
+ {"version":3,"file":"vrt-compare.component.d.ts","sourceRoot":"","sources":["../src/vrt-compare.component.ts"],"names":[],"mappings":"AAUA,OAAO,EAA0B,KAAK,IAAI,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;;AAErE,KAAK,WAAW,GAAG,MAAM,GAAG,cAAc,GAAG,MAAM,CAAC;AAQpD;;;;;GAKG;AACH,qBAiaa,mBAAmB;IAC9B,QAAQ,CAAC,OAAO,wDAAsC;IACtD,QAAQ,CAAC,YAAY,8CAAa;IAElC,SAAS,CAAC,QAAQ,CAAC,aAAa,6DAAoC;IACpE,SAAS,CAAC,QAAQ,CAAC,IAAI,+CAAuB;IAC9C,SAAS,CAAC,QAAQ,CAAC,IAAI,iDAAc;IAErC,SAAS,CAAC,QAAQ,CAAC,UAAU,8BAAc;IAC3C,SAAS,CAAC,QAAQ,CAAC,KAAK,kBAAS;IAEjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAE1B;IACF,OAAO,CAAC,QAAQ,CAAC,UAAU,CAEzB;IACF,SAAS,CAAC,QAAQ,CAAC,OAAO,qDAExB;IAEF;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,MAAM;;;;;cAUtB;IAEH;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,cAAc,yDAE/B;IAEF,SAAS,CAAC,QAAQ,CAAC,eAAe,yDAEhC;IAEF,6EAA6E;IAC7E,SAAS,CAAC,QAAQ,CAAC,MAAM;;;cAA4C;IAErE,SAAS,CAAC,QAAQ,CAAC,WAAW;;;;;;cAe3B;IAEH,SAAS,CAAC,QAAQ,CAAC,cAAc,gDAgB9B;IAEH,SAAS,CAAC,QAAQ,CAAC,UAAU,8CAM1B;IAEH,yEAAyE;IACzE,SAAS,CAAC,QAAQ,CAAC,OAAO,0CAExB;IAEF;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,WAAW,0CAQ3B;IAEH,qEAAqE;IACrE,SAAS,CAAC,QAAQ,CAAC,UAAU,yDAG1B;IAEH,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;yCA5H7B,mBAAmB;2CAAnB,mBAAmB;CA+H/B"}
@@ -433,7 +433,7 @@ export class VrtCompareComponent {
433
433
  i0.ɵɵconditional(ctx.showToolbar() ? 1 : -1);
434
434
  i0.ɵɵadvance();
435
435
  i0.ɵɵconditional(ctx.variant().status === "excluded" ? 2 : 3);
436
- } }, styles: ["[_nghost-%COMP%] { display: block; min-width: 0; }\n\n .vrt-cmp[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 14px 16px;\n min-width: 0;\n }\n\n .vrt-cmp__bar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n flex-wrap: wrap;\n border-bottom: 1px solid var(--prism-border);\n }\n\n .vrt-cmp__right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding-bottom: 7px;\n }\n\n \n\n .vrt-cmp__modes[_ngcontent-%COMP%] { display: flex; gap: 2px; }\n\n .vrt-cmp__mode[_ngcontent-%COMP%] {\n position: relative;\n padding: 6px 11px 8px;\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n font-size: 12px;\n font-weight: 500;\n color: var(--prism-text-muted);\n transition: color var(--dur-fast);\n }\n .vrt-cmp__mode[_ngcontent-%COMP%]:hover { color: var(--prism-text-2); }\n .vrt-cmp__mode--active[_ngcontent-%COMP%] { color: var(--prism-text); }\n .vrt-cmp__mode--active[_ngcontent-%COMP%]::after {\n content: '';\n position: absolute;\n left: 6px;\n right: 6px;\n bottom: -1px;\n height: 2px;\n border-radius: 1px;\n background: var(--prism-primary);\n }\n\n .vrt-cmp__meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n }\n .vrt-cmp__meta[_ngcontent-%COMP%] > span[_ngcontent-%COMP%] + span[_ngcontent-%COMP%]::before {\n content: '\u00B7';\n margin-right: 8px;\n color: var(--prism-text-ghost);\n }\n .vrt-cmp__ratio[_ngcontent-%COMP%] { color: var(--prism-success); }\n .vrt-cmp__ratio[data-hot][_ngcontent-%COMP%] { color: var(--prism-danger); }\n\n .vrt-cmp__zooms[_ngcontent-%COMP%] {\n display: flex;\n gap: 1px;\n padding: 2px;\n background: var(--prism-input-bg);\n border-radius: var(--radius-md);\n }\n .vrt-cmp__zoom[_ngcontent-%COMP%] {\n padding: 3px 8px;\n border: none;\n background: none;\n border-radius: var(--radius-sm);\n cursor: pointer;\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n transition: color var(--dur-fast), background var(--dur-fast);\n }\n .vrt-cmp__zoom[_ngcontent-%COMP%]:hover { color: var(--prism-text-2); }\n .vrt-cmp__zoom--active[_ngcontent-%COMP%] {\n background: var(--prism-bg-elevated);\n color: var(--prism-text);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);\n }\n\n .vrt-cmp__stage[_ngcontent-%COMP%] {\n display: flex;\n \n\n overflow: auto;\n padding: 14px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-radius: var(--radius-lg);\n min-width: 0;\n \n\n\n\n\n --vrt-gap: 16px;\n --vrt-fit-basis: 100%;\n }\n .vrt-cmp__stage--split[_ngcontent-%COMP%] {\n gap: var(--vrt-gap);\n --vrt-fit-basis: calc((100% - var(--vrt-gap)) / 2);\n }\n\n .vrt-cmp__frame[_ngcontent-%COMP%] {\n margin: 0 auto;\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: none;\n }\n .vrt-cmp__stage--split[_ngcontent-%COMP%] .vrt-cmp__frame[_ngcontent-%COMP%] { margin: 0; }\n\n figure.vrt-cmp__frame[_ngcontent-%COMP%] figcaption[_ngcontent-%COMP%] {\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n }\n\n \n .vrt-cmp__shot[_ngcontent-%COMP%] {\n position: relative;\n border: 1px solid var(--prism-border);\n border-radius: var(--radius-sm);\n overflow: hidden;\n background-image:\n linear-gradient(45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(-45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, var(--prism-border) 75%),\n linear-gradient(-45deg, transparent 75%, var(--prism-border) 75%);\n background-size: 12px 12px;\n background-position: 0 0, 0 6px, 6px -6px, -6px 0;\n }\n\n .vrt-cmp__img[_ngcontent-%COMP%] {\n display: block;\n width: 100%;\n height: auto;\n \n image-rendering: pixelated;\n }\n\n .vrt-cmp__wipe-top[_ngcontent-%COMP%] { position: absolute; inset: 0; }\n .vrt-cmp__wipe-handle[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 2px;\n margin-left: -1px;\n background: var(--prism-primary);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--prism-void) 45%, transparent);\n pointer-events: none;\n }\n\n .vrt-cmp__slider[_ngcontent-%COMP%] {\n width: 100%;\n margin: 0;\n appearance: none;\n background: none;\n cursor: ew-resize;\n }\n .vrt-cmp__slider[_ngcontent-%COMP%]::-webkit-slider-runnable-track {\n height: 4px;\n border-radius: 2px;\n background: var(--prism-input-bg);\n }\n .vrt-cmp__slider[_ngcontent-%COMP%]::-moz-range-track {\n height: 4px;\n border-radius: 2px;\n background: var(--prism-input-bg);\n }\n .vrt-cmp__slider[_ngcontent-%COMP%]::-webkit-slider-thumb {\n appearance: none;\n width: 13px;\n height: 13px;\n margin-top: -4.5px;\n border-radius: 50%;\n background: var(--prism-primary);\n border: 2px solid var(--prism-bg-elevated);\n box-shadow: 0 0 0 1px var(--prism-border-strong);\n }\n .vrt-cmp__slider[_ngcontent-%COMP%]::-moz-range-thumb {\n width: 13px;\n height: 13px;\n border-radius: 50%;\n background: var(--prism-primary);\n border: 2px solid var(--prism-bg-elevated);\n box-shadow: 0 0 0 1px var(--prism-border-strong);\n }\n\n .vrt-cmp__legend[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n }\n\n .vrt-cmp__note[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 3px;\n padding: 10px 12px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-left: 3px solid var(--prism-text-muted);\n border-radius: var(--radius-md);\n font-size: var(--fs-md);\n }\n .vrt-cmp__note[_ngcontent-%COMP%] b[_ngcontent-%COMP%] { font-weight: 600; color: var(--prism-text); }\n .vrt-cmp__note[_ngcontent-%COMP%] span[_ngcontent-%COMP%] { color: var(--prism-text-muted); }\n .vrt-cmp__note--warn[_ngcontent-%COMP%] { border-left-color: var(--prism-warn); }\n \n\n .vrt-cmp__note[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n background: var(--prism-input-bg);\n }"] });
436
+ } }, styles: ["[_nghost-%COMP%] { display: block; min-width: 0; height: 100%; }\n\n \n\n\n\n .vrt-cmp[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 14px 16px;\n min-width: 0;\n height: 100%;\n }\n\n .vrt-cmp__bar[_ngcontent-%COMP%] {\n flex: none;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n flex-wrap: wrap;\n border-bottom: 1px solid var(--prism-border);\n }\n\n .vrt-cmp__right[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n padding-bottom: 7px;\n }\n\n \n\n .vrt-cmp__modes[_ngcontent-%COMP%] { display: flex; gap: 2px; }\n\n .vrt-cmp__mode[_ngcontent-%COMP%] {\n position: relative;\n padding: 6px 11px 8px;\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n font-size: 12px;\n font-weight: 500;\n color: var(--prism-text-muted);\n transition: color var(--dur-fast);\n }\n .vrt-cmp__mode[_ngcontent-%COMP%]:hover { color: var(--prism-text-2); }\n .vrt-cmp__mode--active[_ngcontent-%COMP%] { color: var(--prism-text); }\n .vrt-cmp__mode--active[_ngcontent-%COMP%]::after {\n content: '';\n position: absolute;\n left: 6px;\n right: 6px;\n bottom: -1px;\n height: 2px;\n border-radius: 1px;\n background: var(--prism-primary);\n }\n\n .vrt-cmp__meta[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n }\n .vrt-cmp__meta[_ngcontent-%COMP%] > span[_ngcontent-%COMP%] + span[_ngcontent-%COMP%]::before {\n content: '\u00B7';\n margin-right: 8px;\n color: var(--prism-text-ghost);\n }\n .vrt-cmp__ratio[_ngcontent-%COMP%] { color: var(--prism-success); }\n .vrt-cmp__ratio[data-hot][_ngcontent-%COMP%] { color: var(--prism-danger); }\n\n .vrt-cmp__zooms[_ngcontent-%COMP%] {\n display: flex;\n gap: 1px;\n padding: 2px;\n background: var(--prism-input-bg);\n border-radius: var(--radius-md);\n }\n .vrt-cmp__zoom[_ngcontent-%COMP%] {\n padding: 3px 8px;\n border: none;\n background: none;\n border-radius: var(--radius-sm);\n cursor: pointer;\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n transition: color var(--dur-fast), background var(--dur-fast);\n }\n .vrt-cmp__zoom[_ngcontent-%COMP%]:hover { color: var(--prism-text-2); }\n .vrt-cmp__zoom--active[_ngcontent-%COMP%] {\n background: var(--prism-bg-elevated);\n color: var(--prism-text);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);\n }\n\n .vrt-cmp__stage[_ngcontent-%COMP%] {\n \n\n\n flex: 1;\n min-height: 140px;\n display: flex;\n \n\n overflow: auto;\n padding: 14px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-radius: var(--radius-lg);\n min-width: 0;\n \n\n\n\n\n --vrt-gap: 16px;\n --vrt-fit-basis: 100%;\n }\n .vrt-cmp__stage--split[_ngcontent-%COMP%] {\n gap: var(--vrt-gap);\n --vrt-fit-basis: calc((100% - var(--vrt-gap)) / 2);\n }\n\n \n\n\n .vrt-cmp__frame[_ngcontent-%COMP%] {\n margin: auto;\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: none;\n }\n .vrt-cmp__stage--split[_ngcontent-%COMP%] .vrt-cmp__frame[_ngcontent-%COMP%] { margin: auto 0; }\n\n figure.vrt-cmp__frame[_ngcontent-%COMP%] figcaption[_ngcontent-%COMP%] {\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n }\n\n \n .vrt-cmp__shot[_ngcontent-%COMP%] {\n position: relative;\n border: 1px solid var(--prism-border);\n border-radius: var(--radius-sm);\n overflow: hidden;\n background-image:\n linear-gradient(45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(-45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, var(--prism-border) 75%),\n linear-gradient(-45deg, transparent 75%, var(--prism-border) 75%);\n background-size: 12px 12px;\n background-position: 0 0, 0 6px, 6px -6px, -6px 0;\n }\n\n .vrt-cmp__img[_ngcontent-%COMP%] {\n display: block;\n width: 100%;\n height: auto;\n \n image-rendering: pixelated;\n }\n\n .vrt-cmp__wipe-top[_ngcontent-%COMP%] { position: absolute; inset: 0; }\n .vrt-cmp__wipe-handle[_ngcontent-%COMP%] {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 2px;\n margin-left: -1px;\n background: var(--prism-primary);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--prism-void) 45%, transparent);\n pointer-events: none;\n }\n\n .vrt-cmp__slider[_ngcontent-%COMP%] {\n width: 100%;\n margin: 0;\n appearance: none;\n background: none;\n cursor: ew-resize;\n }\n .vrt-cmp__slider[_ngcontent-%COMP%]::-webkit-slider-runnable-track {\n height: 4px;\n border-radius: 2px;\n background: var(--prism-input-bg);\n }\n .vrt-cmp__slider[_ngcontent-%COMP%]::-moz-range-track {\n height: 4px;\n border-radius: 2px;\n background: var(--prism-input-bg);\n }\n .vrt-cmp__slider[_ngcontent-%COMP%]::-webkit-slider-thumb {\n appearance: none;\n width: 13px;\n height: 13px;\n margin-top: -4.5px;\n border-radius: 50%;\n background: var(--prism-primary);\n border: 2px solid var(--prism-bg-elevated);\n box-shadow: 0 0 0 1px var(--prism-border-strong);\n }\n .vrt-cmp__slider[_ngcontent-%COMP%]::-moz-range-thumb {\n width: 13px;\n height: 13px;\n border-radius: 50%;\n background: var(--prism-primary);\n border: 2px solid var(--prism-bg-elevated);\n box-shadow: 0 0 0 1px var(--prism-border-strong);\n }\n\n .vrt-cmp__legend[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n }\n\n .vrt-cmp__note[_ngcontent-%COMP%] {\n flex: none;\n display: flex;\n flex-direction: column;\n gap: 3px;\n padding: 10px 12px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-left: 3px solid var(--prism-text-muted);\n border-radius: var(--radius-md);\n font-size: var(--fs-md);\n }\n .vrt-cmp__note[_ngcontent-%COMP%] b[_ngcontent-%COMP%] { font-weight: 600; color: var(--prism-text); }\n .vrt-cmp__note[_ngcontent-%COMP%] span[_ngcontent-%COMP%] { color: var(--prism-text-muted); }\n .vrt-cmp__note--warn[_ngcontent-%COMP%] { border-left-color: var(--prism-warn); }\n \n\n .vrt-cmp__note[_ngcontent-%COMP%] code[_ngcontent-%COMP%] {\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n background: var(--prism-input-bg);\n }"] });
437
437
  }
438
438
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(VrtCompareComponent, [{
439
439
  type: Component,
@@ -597,6 +597,6 @@ export class VrtCompareComponent {
597
597
  </div>
598
598
  } }
599
599
  </div>
600
- `, styles: ["\n :host { display: block; min-width: 0; }\n\n .vrt-cmp {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 14px 16px;\n min-width: 0;\n }\n\n .vrt-cmp__bar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n flex-wrap: wrap;\n border-bottom: 1px solid var(--prism-border);\n }\n\n .vrt-cmp__right {\n display: flex;\n align-items: center;\n gap: 12px;\n padding-bottom: 7px;\n }\n\n /* Mode switching is the same act as the a11y panel's sub-tabs, so it gets\n the same underline treatment rather than a third kind of button. */\n .vrt-cmp__modes { display: flex; gap: 2px; }\n\n .vrt-cmp__mode {\n position: relative;\n padding: 6px 11px 8px;\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n font-size: 12px;\n font-weight: 500;\n color: var(--prism-text-muted);\n transition: color var(--dur-fast);\n }\n .vrt-cmp__mode:hover { color: var(--prism-text-2); }\n .vrt-cmp__mode--active { color: var(--prism-text); }\n .vrt-cmp__mode--active::after {\n content: '';\n position: absolute;\n left: 6px;\n right: 6px;\n bottom: -1px;\n height: 2px;\n border-radius: 1px;\n background: var(--prism-primary);\n }\n\n .vrt-cmp__meta {\n display: flex;\n align-items: center;\n gap: 8px;\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n }\n .vrt-cmp__meta > span + span::before {\n content: '\u00B7';\n margin-right: 8px;\n color: var(--prism-text-ghost);\n }\n .vrt-cmp__ratio { color: var(--prism-success); }\n .vrt-cmp__ratio[data-hot] { color: var(--prism-danger); }\n\n .vrt-cmp__zooms {\n display: flex;\n gap: 1px;\n padding: 2px;\n background: var(--prism-input-bg);\n border-radius: var(--radius-md);\n }\n .vrt-cmp__zoom {\n padding: 3px 8px;\n border: none;\n background: none;\n border-radius: var(--radius-sm);\n cursor: pointer;\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n transition: color var(--dur-fast), background var(--dur-fast);\n }\n .vrt-cmp__zoom:hover { color: var(--prism-text-2); }\n .vrt-cmp__zoom--active {\n background: var(--prism-bg-elevated);\n color: var(--prism-text);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);\n }\n\n .vrt-cmp__stage {\n display: flex;\n /* overflow-safe centring: justify-content would clip the left edge of an\n image wider than the stage, margin auto scrolls to it instead. */\n overflow: auto;\n padding: 14px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-radius: var(--radius-lg);\n min-width: 0;\n /* How wide one frame may grow under the fit zoom. Side by side there are\n two of them, so each gets half the stage minus the gap. This is an\n inherited custom property rather than flex-grow, because a growing\n flex item ignores its own width -- which is exactly what the explicit\n zoom steps set. */\n --vrt-gap: 16px;\n --vrt-fit-basis: 100%;\n }\n .vrt-cmp__stage--split {\n gap: var(--vrt-gap);\n --vrt-fit-basis: calc((100% - var(--vrt-gap)) / 2);\n }\n\n .vrt-cmp__frame {\n margin: 0 auto;\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: none;\n }\n .vrt-cmp__stage--split .vrt-cmp__frame { margin: 0; }\n\n figure.vrt-cmp__frame figcaption {\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n }\n\n /* A checkerboard makes transparent regions of a PNG legible. */\n .vrt-cmp__shot {\n position: relative;\n border: 1px solid var(--prism-border);\n border-radius: var(--radius-sm);\n overflow: hidden;\n background-image:\n linear-gradient(45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(-45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, var(--prism-border) 75%),\n linear-gradient(-45deg, transparent 75%, var(--prism-border) 75%);\n background-size: 12px 12px;\n background-position: 0 0, 0 6px, 6px -6px, -6px 0;\n }\n\n .vrt-cmp__img {\n display: block;\n width: 100%;\n height: auto;\n /* Zooming a diff is only useful if the pixels stay pixels. */\n image-rendering: pixelated;\n }\n\n .vrt-cmp__wipe-top { position: absolute; inset: 0; }\n .vrt-cmp__wipe-handle {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 2px;\n margin-left: -1px;\n background: var(--prism-primary);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--prism-void) 45%, transparent);\n pointer-events: none;\n }\n\n .vrt-cmp__slider {\n width: 100%;\n margin: 0;\n appearance: none;\n background: none;\n cursor: ew-resize;\n }\n .vrt-cmp__slider::-webkit-slider-runnable-track {\n height: 4px;\n border-radius: 2px;\n background: var(--prism-input-bg);\n }\n .vrt-cmp__slider::-moz-range-track {\n height: 4px;\n border-radius: 2px;\n background: var(--prism-input-bg);\n }\n .vrt-cmp__slider::-webkit-slider-thumb {\n appearance: none;\n width: 13px;\n height: 13px;\n margin-top: -4.5px;\n border-radius: 50%;\n background: var(--prism-primary);\n border: 2px solid var(--prism-bg-elevated);\n box-shadow: 0 0 0 1px var(--prism-border-strong);\n }\n .vrt-cmp__slider::-moz-range-thumb {\n width: 13px;\n height: 13px;\n border-radius: 50%;\n background: var(--prism-primary);\n border: 2px solid var(--prism-bg-elevated);\n box-shadow: 0 0 0 1px var(--prism-border-strong);\n }\n\n .vrt-cmp__legend {\n display: flex;\n justify-content: space-between;\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n }\n\n .vrt-cmp__note {\n display: flex;\n flex-direction: column;\n gap: 3px;\n padding: 10px 12px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-left: 3px solid var(--prism-text-muted);\n border-radius: var(--radius-md);\n font-size: var(--fs-md);\n }\n .vrt-cmp__note b { font-weight: 600; color: var(--prism-text); }\n .vrt-cmp__note span { color: var(--prism-text-muted); }\n .vrt-cmp__note--warn { border-left-color: var(--prism-warn); }\n /* Matches the panel shell's own inline code, so a background name reads\n the same wherever it appears. */\n .vrt-cmp__note code {\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n background: var(--prism-input-bg);\n }\n "] }]
600
+ `, styles: ["\n :host { display: block; min-width: 0; height: 100%; }\n\n /* Fills the viewer column rather than growing with its content, so the\n stage gets whatever height is left over instead of the panel scrolling.\n The toolbar then stays put while a large capture scrolls inside the\n stage, which is the one place scrolling belongs here. */\n .vrt-cmp {\n display: flex;\n flex-direction: column;\n gap: 12px;\n padding: 14px 16px;\n min-width: 0;\n height: 100%;\n }\n\n .vrt-cmp__bar {\n flex: none;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n flex-wrap: wrap;\n border-bottom: 1px solid var(--prism-border);\n }\n\n .vrt-cmp__right {\n display: flex;\n align-items: center;\n gap: 12px;\n padding-bottom: 7px;\n }\n\n /* Mode switching is the same act as the a11y panel's sub-tabs, so it gets\n the same underline treatment rather than a third kind of button. */\n .vrt-cmp__modes { display: flex; gap: 2px; }\n\n .vrt-cmp__mode {\n position: relative;\n padding: 6px 11px 8px;\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n font-size: 12px;\n font-weight: 500;\n color: var(--prism-text-muted);\n transition: color var(--dur-fast);\n }\n .vrt-cmp__mode:hover { color: var(--prism-text-2); }\n .vrt-cmp__mode--active { color: var(--prism-text); }\n .vrt-cmp__mode--active::after {\n content: '';\n position: absolute;\n left: 6px;\n right: 6px;\n bottom: -1px;\n height: 2px;\n border-radius: 1px;\n background: var(--prism-primary);\n }\n\n .vrt-cmp__meta {\n display: flex;\n align-items: center;\n gap: 8px;\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n }\n .vrt-cmp__meta > span + span::before {\n content: '\u00B7';\n margin-right: 8px;\n color: var(--prism-text-ghost);\n }\n .vrt-cmp__ratio { color: var(--prism-success); }\n .vrt-cmp__ratio[data-hot] { color: var(--prism-danger); }\n\n .vrt-cmp__zooms {\n display: flex;\n gap: 1px;\n padding: 2px;\n background: var(--prism-input-bg);\n border-radius: var(--radius-md);\n }\n .vrt-cmp__zoom {\n padding: 3px 8px;\n border: none;\n background: none;\n border-radius: var(--radius-sm);\n cursor: pointer;\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n transition: color var(--dur-fast), background var(--dur-fast);\n }\n .vrt-cmp__zoom:hover { color: var(--prism-text-2); }\n .vrt-cmp__zoom--active {\n background: var(--prism-bg-elevated);\n color: var(--prism-text);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.18);\n }\n\n .vrt-cmp__stage {\n /* Takes the leftover height, but only down to a floor \u2014 below that the\n viewer column scrolls instead, because a stage squashed to nothing is\n not a comparison. */\n flex: 1;\n min-height: 140px;\n display: flex;\n /* overflow-safe centring: justify-content would clip the left edge of an\n image wider than the stage, margin auto scrolls to it instead. */\n overflow: auto;\n padding: 14px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-radius: var(--radius-lg);\n min-width: 0;\n /* How wide one frame may grow under the fit zoom. Side by side there are\n two of them, so each gets half the stage minus the gap. This is an\n inherited custom property rather than flex-grow, because a growing\n flex item ignores its own width -- which is exactly what the explicit\n zoom steps set. */\n --vrt-gap: 16px;\n --vrt-fit-basis: 100%;\n }\n .vrt-cmp__stage--split {\n gap: var(--vrt-gap);\n --vrt-fit-basis: calc((100% - var(--vrt-gap)) / 2);\n }\n\n /* auto margins rather than the stage centring its children: with\n overflow:auto on the stage, centring clips the leading edge of\n anything larger than it, while auto margins scroll to it. */\n .vrt-cmp__frame {\n margin: auto;\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: none;\n }\n .vrt-cmp__stage--split .vrt-cmp__frame { margin: auto 0; }\n\n figure.vrt-cmp__frame figcaption {\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n }\n\n /* A checkerboard makes transparent regions of a PNG legible. */\n .vrt-cmp__shot {\n position: relative;\n border: 1px solid var(--prism-border);\n border-radius: var(--radius-sm);\n overflow: hidden;\n background-image:\n linear-gradient(45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(-45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, var(--prism-border) 75%),\n linear-gradient(-45deg, transparent 75%, var(--prism-border) 75%);\n background-size: 12px 12px;\n background-position: 0 0, 0 6px, 6px -6px, -6px 0;\n }\n\n .vrt-cmp__img {\n display: block;\n width: 100%;\n height: auto;\n /* Zooming a diff is only useful if the pixels stay pixels. */\n image-rendering: pixelated;\n }\n\n .vrt-cmp__wipe-top { position: absolute; inset: 0; }\n .vrt-cmp__wipe-handle {\n position: absolute;\n top: 0;\n bottom: 0;\n width: 2px;\n margin-left: -1px;\n background: var(--prism-primary);\n box-shadow: 0 0 0 1px color-mix(in srgb, var(--prism-void) 45%, transparent);\n pointer-events: none;\n }\n\n .vrt-cmp__slider {\n width: 100%;\n margin: 0;\n appearance: none;\n background: none;\n cursor: ew-resize;\n }\n .vrt-cmp__slider::-webkit-slider-runnable-track {\n height: 4px;\n border-radius: 2px;\n background: var(--prism-input-bg);\n }\n .vrt-cmp__slider::-moz-range-track {\n height: 4px;\n border-radius: 2px;\n background: var(--prism-input-bg);\n }\n .vrt-cmp__slider::-webkit-slider-thumb {\n appearance: none;\n width: 13px;\n height: 13px;\n margin-top: -4.5px;\n border-radius: 50%;\n background: var(--prism-primary);\n border: 2px solid var(--prism-bg-elevated);\n box-shadow: 0 0 0 1px var(--prism-border-strong);\n }\n .vrt-cmp__slider::-moz-range-thumb {\n width: 13px;\n height: 13px;\n border-radius: 50%;\n background: var(--prism-primary);\n border: 2px solid var(--prism-bg-elevated);\n box-shadow: 0 0 0 1px var(--prism-border-strong);\n }\n\n .vrt-cmp__legend {\n display: flex;\n justify-content: space-between;\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n }\n\n .vrt-cmp__note {\n flex: none;\n display: flex;\n flex-direction: column;\n gap: 3px;\n padding: 10px 12px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-left: 3px solid var(--prism-text-muted);\n border-radius: var(--radius-md);\n font-size: var(--fs-md);\n }\n .vrt-cmp__note b { font-weight: 600; color: var(--prism-text); }\n .vrt-cmp__note span { color: var(--prism-text-muted); }\n .vrt-cmp__note--warn { border-left-color: var(--prism-warn); }\n /* Matches the panel shell's own inline code, so a background name reads\n the same wherever it appears. */\n .vrt-cmp__note code {\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n padding: 1px 5px;\n border-radius: var(--radius-xs);\n background: var(--prism-input-bg);\n }\n "] }]
601
601
  }], null, { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: true }] }], assetBaseUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "assetBaseUrl", required: false }] }] }); })();
602
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(VrtCompareComponent, { className: "VrtCompareComponent", filePath: "vrt-compare.component.ts", lineNumber: 430 }); })();
602
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(VrtCompareComponent, { className: "VrtCompareComponent", filePath: "vrt-compare.component.ts", lineNumber: 445 }); })();
@@ -17,26 +17,29 @@ export interface VrtSummary {
17
17
  /** Largest diff among compared variants; `null` when nothing was compared. */
18
18
  maxDiffRatio: number | null;
19
19
  }
20
- export interface VrtTile {
21
- key: string;
20
+ export interface VrtSegment {
21
+ key: VrtStatus;
22
22
  label: string;
23
- value: string;
24
- /** How far the tile's bar fills, 0–1. */
25
- ratio: number;
23
+ /** Raw count — the bar weights its slices by this, so it needs no ratio. */
24
+ count: number;
26
25
  tone: VrtTone;
27
26
  }
28
27
  /** Counts and the worst diff, derived from the variants already in `meta`. */
29
28
  export declare function summarize(variants: readonly VrtVariantResult[]): VrtSummary;
30
29
  /**
31
- * The tiles the summary strip renders.
30
+ * The slices of the composition bar, worst news first.
32
31
  *
33
- * `Changed` and `Unchanged` are always present: a strip that loses a column
34
- * when a run goes green would make the panel change shape with its content,
35
- * and "no Changed tile" is easy to misread as "no data". Everything else
36
- * appears only when it has something to report, so a library that never
37
- * excludes a variant is never asked to wonder what `Excluded` means.
32
+ * Only statuses that actually occurred. The tile strip this replaced had to
33
+ * keep `changed` and `unchanged` present at zero so the panel would not change
34
+ * shape with its content a bar has no such problem, because a zero count is
35
+ * a zero-width slice. Carrying it would add an invisible segment and a legend
36
+ * entry claiming a colour nothing on screen has.
37
+ *
38
+ * Counts rather than ratios: the bar divides itself with `flex-grow`, so it
39
+ * needs the weights, not pre-divided shares, and an empty run yields an empty
40
+ * bar instead of a division by zero.
38
41
  */
39
- export declare function summaryTiles(summary: VrtSummary): VrtTile[];
42
+ export declare function summarySegments(summary: VrtSummary): VrtSegment[];
40
43
  /**
41
44
  * A ratio as the percentage a reviewer reads.
42
45
  *
@@ -1 +1 @@
1
- {"version":3,"file":"vrt-summarize.d.ts","sourceRoot":"","sources":["../src/vrt-summarize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhF;;;;;;GAMG;AACH,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAE1E,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAMlD,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAMlD,CAAC;AAWF,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAClC,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAED,8EAA8E;AAC9E,wBAAgB,SAAS,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,GAAG,UAAU,CAyB3E;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,EAAE,CAiC3D;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKnD"}
1
+ {"version":3,"file":"vrt-summarize.d.ts","sourceRoot":"","sources":["../src/vrt-summarize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAEhF;;;;;;GAMG;AACH,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAE1E,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAMlD,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,MAAM,CAAC,SAAS,EAAE,OAAO,CAMlD,CAAC;AAWF,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAClC,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,SAAS,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,OAAO,CAAC;CACf;AAED,8EAA8E;AAC9E,wBAAgB,SAAS,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,GAAG,UAAU,CAyB3E;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,UAAU,GAAG,UAAU,EAAE,CASjE;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKnD"}
@@ -45,40 +45,25 @@ export function summarize(variants) {
45
45
  return { total: variants.length, counts, compared, maxDiffRatio };
46
46
  }
47
47
  /**
48
- * The tiles the summary strip renders.
48
+ * The slices of the composition bar, worst news first.
49
49
  *
50
- * `Changed` and `Unchanged` are always present: a strip that loses a column
51
- * when a run goes green would make the panel change shape with its content,
52
- * and "no Changed tile" is easy to misread as "no data". Everything else
53
- * appears only when it has something to report, so a library that never
54
- * excludes a variant is never asked to wonder what `Excluded` means.
50
+ * Only statuses that actually occurred. The tile strip this replaced had to
51
+ * keep `changed` and `unchanged` present at zero so the panel would not change
52
+ * shape with its content a bar has no such problem, because a zero count is
53
+ * a zero-width slice. Carrying it would add an invisible segment and a legend
54
+ * entry claiming a colour nothing on screen has.
55
+ *
56
+ * Counts rather than ratios: the bar divides itself with `flex-grow`, so it
57
+ * needs the weights, not pre-divided shares, and an empty run yields an empty
58
+ * bar instead of a division by zero.
55
59
  */
56
- export function summaryTiles(summary) {
57
- const share = (count) => summary.total === 0 ? 0 : count / summary.total;
58
- const tiles = STATUS_ORDER.filter((status) => summary.counts[status] > 0 ||
59
- status === 'changed' ||
60
- status === 'unchanged').map((status) => {
61
- const count = summary.counts[status];
62
- return {
63
- key: status,
64
- label: STATUS_LABEL[status],
65
- value: String(count),
66
- ratio: share(count),
67
- // A count of zero is the absence of news, not news in that colour:
68
- // "Changed 0" rendered in danger red screams the opposite of what it says.
69
- tone: count === 0 ? 'muted' : STATUS_TONE[status],
70
- };
71
- });
72
- if (summary.maxDiffRatio !== null) {
73
- tiles.push({
74
- key: 'max-diff',
75
- label: 'Max diff',
76
- value: formatPercent(summary.maxDiffRatio),
77
- ratio: summary.maxDiffRatio,
78
- tone: summary.maxDiffRatio > 0 ? 'danger' : 'success',
79
- });
80
- }
81
- return tiles;
60
+ export function summarySegments(summary) {
61
+ return STATUS_ORDER.filter((status) => summary.counts[status] > 0).map((status) => ({
62
+ key: status,
63
+ label: STATUS_LABEL[status],
64
+ count: summary.counts[status],
65
+ tone: STATUS_TONE[status],
66
+ }));
82
67
  }
83
68
  /**
84
69
  * A ratio as the percentage a reviewer reads.
@@ -1,16 +1,39 @@
1
1
  import { type VrtSummary } from './vrt-summarize.js';
2
2
  import * as i0 from "@angular/core";
3
3
  /**
4
- * The strip that opens the panel.
4
+ * The run's headline, sitting above the variant list.
5
5
  *
6
- * Every other ng-prism panel leads with a summary the a11y score ring, the
7
- * coverage stat row so the visual regression panel does too, and it borrows
8
- * their card, micro-label and mono-numeral treatment rather than inventing a
9
- * third one.
6
+ * It lives in the 250px column rather than in a band across the panel, and
7
+ * that placement is the whole design. This panel docks at the bottom of the
8
+ * app at 260px by default, which leaves ~228px inside it; a full-width summary
9
+ * strip took ~89px of that before the comparison had rendered a single pixel.
10
+ * In the narrow column it costs the variant list about one row and costs the
11
+ * image nothing — and the image is what the panel is for.
12
+ *
13
+ * The cost of that is precision, so the split is deliberate: the bar carries
14
+ * the *composition* (which statuses, in what proportion) where a glance is
15
+ * enough, and the line under it carries the two figures a reviewer acts on.
16
+ * Exact per-status counts live one place over, in the list itself, where every
17
+ * row already states its own status — repeating them here would be the tile
18
+ * strip again, in a narrower column.
10
19
  */
11
20
  export declare class VrtSummaryComponent {
12
21
  readonly summary: import("@angular/core").InputSignal<VrtSummary>;
13
- protected readonly tiles: import("@angular/core").Signal<import("./vrt-summarize.js").VrtTile[]>;
22
+ protected readonly segments: import("@angular/core").Signal<import("./vrt-summarize.js").VrtSegment[]>;
23
+ protected readonly changed: import("@angular/core").Signal<number>;
24
+ protected readonly maxDiff: import("@angular/core").Signal<{
25
+ value: string;
26
+ tone: string;
27
+ } | null>;
28
+ /**
29
+ * The bar's content as a sentence.
30
+ *
31
+ * The slices are the only place the per-status breakdown is shown, and they
32
+ * are colour and proportion — nothing a screen reader can read. Each slice
33
+ * carries a `title` for a pointer; this carries the same thing for everyone
34
+ * else.
35
+ */
36
+ protected readonly barLabel: import("@angular/core").Signal<string>;
14
37
  static ɵfac: i0.ɵɵFactoryDeclaration<VrtSummaryComponent, never>;
15
38
  static ɵcmp: i0.ɵɵComponentDeclaration<VrtSummaryComponent, "prism-vrt-summary", never, { "summary": { "alias": "summary"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
16
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"vrt-summary.component.d.ts","sourceRoot":"","sources":["../src/vrt-summary.component.ts"],"names":[],"mappings":"AAMA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;;AAEnE;;;;;;;GAOG;AACH,qBAiFa,mBAAmB;IAC9B,QAAQ,CAAC,OAAO,kDAAgC;IAEhD,SAAS,CAAC,QAAQ,CAAC,KAAK,yEAAgD;yCAH7D,mBAAmB;2CAAnB,mBAAmB;CAI/B"}
1
+ {"version":3,"file":"vrt-summary.component.d.ts","sourceRoot":"","sources":["../src/vrt-summary.component.ts"],"names":[],"mappings":"AAMA,OAAO,EAGL,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;;AAE5B;;;;;;;;;;;;;;;;GAgBG;AACH,qBA2Ea,mBAAmB;IAC9B,QAAQ,CAAC,OAAO,kDAAgC;IAEhD,SAAS,CAAC,QAAQ,CAAC,QAAQ,4EAAmD;IAE9E,SAAS,CAAC,QAAQ,CAAC,OAAO,yCAAiD;IAE3E,SAAS,CAAC,QAAQ,CAAC,OAAO;;;cAOvB;IAEH;;;;;;;OAOG;IACH,SAAS,CAAC,QAAQ,CAAC,QAAQ,yCAMzB;yCA9BS,mBAAmB;2CAAnB,mBAAmB;CA+B/B"}
@@ -1,67 +1,133 @@
1
1
  import { ChangeDetectionStrategy, Component, computed, input, } from '@angular/core';
2
- import { summaryTiles } from './vrt-summarize.js';
2
+ import { formatPercent, summarySegments, } from './vrt-summarize.js';
3
3
  import * as i0 from "@angular/core";
4
4
  const _forTrack0 = ($index, $item) => $item.key;
5
- function VrtSummaryComponent_For_2_Template(rf, ctx) { if (rf & 1) {
6
- i0.ɵɵdomElementStart(0, "div", 1)(1, "div", 2);
7
- i0.ɵɵtext(2);
8
- i0.ɵɵdomElementEnd();
9
- i0.ɵɵdomElementStart(3, "div", 3);
10
- i0.ɵɵtext(4);
11
- i0.ɵɵdomElementEnd();
12
- i0.ɵɵdomElementStart(5, "div", 4);
13
- i0.ɵɵdomElement(6, "div", 5);
5
+ function VrtSummaryComponent_For_3_Template(rf, ctx) { if (rf & 1) {
6
+ i0.ɵɵdomElement(0, "div", 5);
7
+ } if (rf & 2) {
8
+ const segment_r1 = ctx.$implicit;
9
+ i0.ɵɵstyleProp("flex-grow", segment_r1.count);
10
+ i0.ɵɵdomProperty("title", segment_r1.label + ": " + segment_r1.count);
11
+ i0.ɵɵattribute("data-tone", segment_r1.tone);
12
+ } }
13
+ function VrtSummaryComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
14
+ i0.ɵɵdomElementStart(0, "span", 4);
15
+ i0.ɵɵtext(1, " max ");
16
+ i0.ɵɵdomElementStart(2, "b");
17
+ i0.ɵɵtext(3);
14
18
  i0.ɵɵdomElementEnd()();
15
19
  } if (rf & 2) {
16
- const tile_r1 = ctx.$implicit;
17
- i0.ɵɵattribute("data-tone", tile_r1.tone);
18
- i0.ɵɵadvance(2);
19
- i0.ɵɵtextInterpolate(tile_r1.label);
20
+ const max_r2 = ctx;
20
21
  i0.ɵɵadvance(2);
21
- i0.ɵɵtextInterpolate(tile_r1.value);
22
- i0.ɵɵadvance(2);
23
- i0.ɵɵstyleProp("width", tile_r1.ratio * 100, "%");
22
+ i0.ɵɵattribute("data-tone", max_r2.tone);
23
+ i0.ɵɵadvance();
24
+ i0.ɵɵtextInterpolate(max_r2.value);
24
25
  } }
25
26
  /**
26
- * The strip that opens the panel.
27
+ * The run's headline, sitting above the variant list.
28
+ *
29
+ * It lives in the 250px column rather than in a band across the panel, and
30
+ * that placement is the whole design. This panel docks at the bottom of the
31
+ * app at 260px by default, which leaves ~228px inside it; a full-width summary
32
+ * strip took ~89px of that before the comparison had rendered a single pixel.
33
+ * In the narrow column it costs the variant list about one row and costs the
34
+ * image nothing — and the image is what the panel is for.
27
35
  *
28
- * Every other ng-prism panel leads with a summary the a11y score ring, the
29
- * coverage stat row so the visual regression panel does too, and it borrows
30
- * their card, micro-label and mono-numeral treatment rather than inventing a
31
- * third one.
36
+ * The cost of that is precision, so the split is deliberate: the bar carries
37
+ * the *composition* (which statuses, in what proportion) where a glance is
38
+ * enough, and the line under it carries the two figures a reviewer acts on.
39
+ * Exact per-status counts live one place over, in the list itself, where every
40
+ * row already states its own status — repeating them here would be the tile
41
+ * strip again, in a narrower column.
32
42
  */
33
43
  export class VrtSummaryComponent {
34
44
  summary = input.required(/* @ts-ignore */
35
45
  ...(ngDevMode ? [{ debugName: "summary" }] : /* istanbul ignore next */ []));
36
- tiles = computed(() => summaryTiles(this.summary()), /* @ts-ignore */
37
- ...(ngDevMode ? [{ debugName: "tiles" }] : /* istanbul ignore next */ []));
46
+ segments = computed(() => summarySegments(this.summary()), /* @ts-ignore */
47
+ ...(ngDevMode ? [{ debugName: "segments" }] : /* istanbul ignore next */ []));
48
+ changed = computed(() => this.summary().counts.changed, /* @ts-ignore */
49
+ ...(ngDevMode ? [{ debugName: "changed" }] : /* istanbul ignore next */ []));
50
+ maxDiff = computed(() => {
51
+ const ratio = this.summary().maxDiffRatio;
52
+ if (ratio === null)
53
+ return null;
54
+ return {
55
+ value: formatPercent(ratio),
56
+ tone: ratio > 0 ? 'danger' : 'success',
57
+ };
58
+ }, /* @ts-ignore */
59
+ ...(ngDevMode ? [{ debugName: "maxDiff" }] : /* istanbul ignore next */ []));
60
+ /**
61
+ * The bar's content as a sentence.
62
+ *
63
+ * The slices are the only place the per-status breakdown is shown, and they
64
+ * are colour and proportion — nothing a screen reader can read. Each slice
65
+ * carries a `title` for a pointer; this carries the same thing for everyone
66
+ * else.
67
+ */
68
+ barLabel = computed(() => this.segments().length === 0
69
+ ? 'No variants'
70
+ : this.segments()
71
+ .map((segment) => `${segment.count} ${segment.label.toLowerCase()}`)
72
+ .join(', '), /* @ts-ignore */
73
+ ...(ngDevMode ? [{ debugName: "barLabel" }] : /* istanbul ignore next */ []));
38
74
  static ɵfac = function VrtSummaryComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || VrtSummaryComponent)(); };
39
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: VrtSummaryComponent, selectors: [["prism-vrt-summary"]], inputs: { summary: [1, "summary"] }, decls: 3, vars: 0, consts: [[1, "vrt-sum"], [1, "vrt-sum__tile"], [1, "vrt-sum__label"], [1, "vrt-sum__value"], [1, "vrt-sum__bar"], [1, "vrt-sum__bar-fill"]], template: function VrtSummaryComponent_Template(rf, ctx) { if (rf & 1) {
40
- i0.ɵɵdomElementStart(0, "div", 0);
41
- i0.ɵɵrepeaterCreate(1, VrtSummaryComponent_For_2_Template, 7, 5, "div", 1, _forTrack0);
75
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: VrtSummaryComponent, selectors: [["prism-vrt-summary"]], inputs: { summary: [1, "summary"] }, decls: 10, vars: 5, consts: [[1, "vrt-sum"], ["role", "img", 1, "vrt-sum__bar"], [1, "vrt-sum__seg", 3, "flex-grow", "title"], [1, "vrt-sum__line"], [1, "vrt-sum__stat"], [1, "vrt-sum__seg", 3, "title"]], template: function VrtSummaryComponent_Template(rf, ctx) { if (rf & 1) {
76
+ i0.ɵɵdomElementStart(0, "div", 0)(1, "div", 1);
77
+ i0.ɵɵrepeaterCreate(2, VrtSummaryComponent_For_3_Template, 1, 4, "div", 2, _forTrack0);
78
+ i0.ɵɵdomElementEnd();
79
+ i0.ɵɵdomElementStart(4, "div", 3)(5, "span", 4)(6, "b");
80
+ i0.ɵɵtext(7);
42
81
  i0.ɵɵdomElementEnd();
82
+ i0.ɵɵtext(8);
83
+ i0.ɵɵdomElementEnd();
84
+ i0.ɵɵconditionalCreate(9, VrtSummaryComponent_Conditional_9_Template, 4, 2, "span", 4);
85
+ i0.ɵɵdomElementEnd()();
43
86
  } if (rf & 2) {
87
+ let tmp_5_0;
88
+ i0.ɵɵadvance();
89
+ i0.ɵɵattribute("aria-label", ctx.barLabel());
90
+ i0.ɵɵadvance();
91
+ i0.ɵɵrepeater(ctx.segments());
92
+ i0.ɵɵadvance(4);
93
+ i0.ɵɵattribute("data-tone", ctx.changed() > 0 ? "danger" : "success");
44
94
  i0.ɵɵadvance();
45
- i0.ɵɵrepeater(ctx.tiles());
46
- } }, styles: ["[_nghost-%COMP%] { display: block; }\n\n .vrt-sum[_ngcontent-%COMP%] {\n display: grid;\n \n\n grid-template-columns: repeat(auto-fit, minmax(118px, 1fr));\n gap: 10px;\n }\n\n .vrt-sum__tile[_ngcontent-%COMP%] {\n padding: 10px 12px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-radius: var(--radius-lg);\n min-width: 0;\n }\n\n .vrt-sum__label[_ngcontent-%COMP%] {\n font-size: 10.5px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n font-weight: 700;\n color: var(--prism-text-ghost);\n margin-bottom: 5px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .vrt-sum__value[_ngcontent-%COMP%] {\n font-family: var(--font-mono);\n font-size: 21px;\n font-weight: 700;\n letter-spacing: -0.02em;\n color: var(--tone-color, var(--prism-text));\n line-height: 1.1;\n }\n\n .vrt-sum__bar[_ngcontent-%COMP%] {\n margin-top: 8px;\n height: 4px;\n background: var(--prism-input-bg);\n border-radius: 2px;\n overflow: hidden;\n }\n .vrt-sum__bar-fill[_ngcontent-%COMP%] {\n height: 100%;\n border-radius: 2px;\n background: var(--tone-color, var(--prism-primary));\n transition: width var(--dur-slow) var(--ease-default);\n }\n\n [data-tone='success'][_ngcontent-%COMP%] { --tone-color: var(--prism-success); }\n [data-tone='danger'][_ngcontent-%COMP%] { --tone-color: var(--prism-danger); }\n [data-tone='warn'][_ngcontent-%COMP%] { --tone-color: var(--prism-warn); }\n [data-tone='neutral'][_ngcontent-%COMP%] { --tone-color: var(--prism-primary); }\n [data-tone='muted'][_ngcontent-%COMP%] { --tone-color: var(--prism-text-muted); }"] });
95
+ i0.ɵɵtextInterpolate(ctx.changed());
96
+ i0.ɵɵadvance();
97
+ i0.ɵɵtextInterpolate1(" of ", ctx.summary().total, " changed ");
98
+ i0.ɵɵadvance();
99
+ i0.ɵɵconditional((tmp_5_0 = ctx.maxDiff()) ? 9 : -1, tmp_5_0);
100
+ } }, styles: ["[_nghost-%COMP%] { display: block; }\n\n .vrt-sum__bar[_ngcontent-%COMP%] {\n display: flex;\n gap: 1px;\n height: 6px;\n border-radius: 3px;\n overflow: hidden;\n background: var(--prism-input-bg);\n }\n \n\n\n .vrt-sum__seg[_ngcontent-%COMP%] {\n flex-basis: 0;\n background: var(--tone-color, var(--prism-text-muted));\n transition: flex-grow var(--dur-slow) var(--ease-default);\n }\n\n .vrt-sum__line[_ngcontent-%COMP%] {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 8px;\n margin-top: 9px;\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n }\n .vrt-sum__stat[_ngcontent-%COMP%] { white-space: nowrap; }\n .vrt-sum__line[_ngcontent-%COMP%] b[_ngcontent-%COMP%] {\n font-weight: 700;\n color: var(--tone-color, var(--prism-text));\n }\n\n [data-tone='success'][_ngcontent-%COMP%] { --tone-color: var(--prism-success); }\n [data-tone='danger'][_ngcontent-%COMP%] { --tone-color: var(--prism-danger); }\n [data-tone='warn'][_ngcontent-%COMP%] { --tone-color: var(--prism-warn); }\n [data-tone='neutral'][_ngcontent-%COMP%] { --tone-color: var(--prism-primary); }\n [data-tone='muted'][_ngcontent-%COMP%] { --tone-color: var(--prism-text-muted); }"] });
47
101
  }
48
102
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(VrtSummaryComponent, [{
49
103
  type: Component,
50
104
  args: [{ selector: 'prism-vrt-summary', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
51
105
  <div class="vrt-sum">
52
- @for (tile of tiles(); track tile.key) {
53
- <div class="vrt-sum__tile" [attr.data-tone]="tile.tone">
54
- <div class="vrt-sum__label">{{ tile.label }}</div>
55
- <div class="vrt-sum__value">{{ tile.value }}</div>
56
- <div class="vrt-sum__bar">
57
- <div
58
- class="vrt-sum__bar-fill"
59
- [style.width.%]="tile.ratio * 100"
60
- ></div>
61
- </div>
106
+ <div class="vrt-sum__bar" role="img" [attr.aria-label]="barLabel()">
107
+ @for (segment of segments(); track segment.key) {
108
+ <div
109
+ class="vrt-sum__seg"
110
+ [attr.data-tone]="segment.tone"
111
+ [style.flex-grow]="segment.count"
112
+ [title]="segment.label + ': ' + segment.count"
113
+ ></div>
114
+ }
115
+ </div>
116
+
117
+ <div class="vrt-sum__line">
118
+ <span class="vrt-sum__stat">
119
+ <b [attr.data-tone]="changed() > 0 ? 'danger' : 'success'">{{
120
+ changed()
121
+ }}</b>
122
+ of {{ summary().total }} changed
123
+ </span>
124
+ @if (maxDiff(); as max) {
125
+ <span class="vrt-sum__stat">
126
+ max <b [attr.data-tone]="max.tone">{{ max.value }}</b>
127
+ </span>
128
+ }
62
129
  </div>
63
- }
64
130
  </div>
65
- `, styles: ["\n :host { display: block; }\n\n .vrt-sum {\n display: grid;\n /* auto-fit rather than a fixed column count: the strip carries between\n two and six tiles depending on what the run produced. */\n grid-template-columns: repeat(auto-fit, minmax(118px, 1fr));\n gap: 10px;\n }\n\n .vrt-sum__tile {\n padding: 10px 12px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-radius: var(--radius-lg);\n min-width: 0;\n }\n\n .vrt-sum__label {\n font-size: 10.5px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n font-weight: 700;\n color: var(--prism-text-ghost);\n margin-bottom: 5px;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .vrt-sum__value {\n font-family: var(--font-mono);\n font-size: 21px;\n font-weight: 700;\n letter-spacing: -0.02em;\n color: var(--tone-color, var(--prism-text));\n line-height: 1.1;\n }\n\n .vrt-sum__bar {\n margin-top: 8px;\n height: 4px;\n background: var(--prism-input-bg);\n border-radius: 2px;\n overflow: hidden;\n }\n .vrt-sum__bar-fill {\n height: 100%;\n border-radius: 2px;\n background: var(--tone-color, var(--prism-primary));\n transition: width var(--dur-slow) var(--ease-default);\n }\n\n [data-tone='success'] { --tone-color: var(--prism-success); }\n [data-tone='danger'] { --tone-color: var(--prism-danger); }\n [data-tone='warn'] { --tone-color: var(--prism-warn); }\n [data-tone='neutral'] { --tone-color: var(--prism-primary); }\n [data-tone='muted'] { --tone-color: var(--prism-text-muted); }\n "] }]
131
+ `, styles: ["\n :host { display: block; }\n\n .vrt-sum__bar {\n display: flex;\n gap: 1px;\n height: 6px;\n border-radius: 3px;\n overflow: hidden;\n background: var(--prism-input-bg);\n }\n /* flex-basis 0 so a slice's width comes from its count alone \u2014 with the\n default of auto an empty div still claims its content box and every\n segment would render the same width. */\n .vrt-sum__seg {\n flex-basis: 0;\n background: var(--tone-color, var(--prism-text-muted));\n transition: flex-grow var(--dur-slow) var(--ease-default);\n }\n\n .vrt-sum__line {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: 8px;\n margin-top: 9px;\n font-family: var(--font-mono);\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n }\n .vrt-sum__stat { white-space: nowrap; }\n .vrt-sum__line b {\n font-weight: 700;\n color: var(--tone-color, var(--prism-text));\n }\n\n [data-tone='success'] { --tone-color: var(--prism-success); }\n [data-tone='danger'] { --tone-color: var(--prism-danger); }\n [data-tone='warn'] { --tone-color: var(--prism-warn); }\n [data-tone='neutral'] { --tone-color: var(--prism-primary); }\n [data-tone='muted'] { --tone-color: var(--prism-text-muted); }\n "] }]
66
132
  }], null, { summary: [{ type: i0.Input, args: [{ isSignal: true, alias: "summary", required: true }] }] }); })();
67
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(VrtSummaryComponent, { className: "VrtSummaryComponent", filePath: "vrt-summary.component.ts", lineNumber: 98 }); })();
133
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(VrtSummaryComponent, { className: "VrtSummaryComponent", filePath: "vrt-summary.component.ts", lineNumber: 105 }); })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-prism/plugin-visual-regression",
3
- "version": "22.2.0-beta.1",
3
+ "version": "22.2.0-beta.3",
4
4
  "description": "Visual regression report panel for ng-prism.",
5
5
  "ng-add": {
6
6
  "save": "devDependencies"