@ng-prism/plugin-perf 22.5.0 → 22.7.0-beta.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.
@@ -1,109 +1,53 @@
1
- import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';
1
+ import { ChangeDetectionStrategy, Component, input } from '@angular/core';
2
+ import { PrismMetricCardComponent, toneForStatus } from '@ng-prism/core';
3
+ import { perfStatus } from '../perf-status.js';
2
4
  import { SparklineComponent } from './sparkline.component.js';
3
5
  import * as i0 from "@angular/core";
4
- function RenderSectionComponent_Conditional_0_Conditional_0_Conditional_43_Template(rf, ctx) { if (rf & 1) {
5
- i0.ɵɵelementStart(0, "div", 7)(1, "div", 8)(2, "div", 3)(3, "span", 4);
6
- i0.ɵɵtext(4);
6
+ function RenderSectionComponent_Conditional_0_Conditional_0_Conditional_6_Template(rf, ctx) { if (rf & 1) {
7
+ i0.ɵɵelementStart(0, "div", 7)(1, "span", 8);
8
+ i0.ɵɵtext(2, "Render time trace");
7
9
  i0.ɵɵelementEnd();
8
- i0.ɵɵelementStart(5, "span", 9);
9
- i0.ɵɵtext(6, "avg ");
10
- i0.ɵɵelementStart(7, "span", 10);
11
- i0.ɵɵtext(8);
10
+ i0.ɵɵelement(3, "perf-sparkline", 9);
12
11
  i0.ɵɵelementEnd();
13
- i0.ɵɵtext(9, " \u00B7 max ");
14
- i0.ɵɵelementStart(10, "span", 10);
15
- i0.ɵɵtext(11);
16
- i0.ɵɵelementEnd()()();
17
- i0.ɵɵelementStart(12, "div", 11);
18
- i0.ɵɵelement(13, "perf-sparkline", 12);
19
- i0.ɵɵelementEnd()()();
20
12
  } if (rf & 2) {
21
13
  const svc_r1 = i0.ɵɵnextContext(2);
22
14
  const ctx_r1 = i0.ɵɵnextContext();
23
- i0.ɵɵadvance(4);
24
- i0.ɵɵtextInterpolate1("Render Time Trace \u2014 Last ", svc_r1.sampleCount(), " Samples");
25
- i0.ɵɵadvance(4);
26
- i0.ɵɵtextInterpolate1("", ctx_r1.formatMs(svc_r1.avgRerender()), " ms");
15
+ i0.ɵɵproperty("title", "Last " + svc_r1.sampleCount() + " samples \u00B7 avg " + ctx_r1.formatMs(svc_r1.avgRerender()) + " ms \u00B7 max " + ctx_r1.formatMs(svc_r1.maxRerender()) + " ms \u00B7 line at " + ctx_r1.warnMs() + " ms");
27
16
  i0.ɵɵadvance(3);
28
- i0.ɵɵtextInterpolate1("", ctx_r1.formatMs(svc_r1.maxRerender()), " ms");
29
- i0.ɵɵadvance(2);
30
17
  i0.ɵɵproperty("samples", svc_r1.rerenders())("thresholdWarn", ctx_r1.warnMs())("height", 80);
31
18
  } }
32
19
  function RenderSectionComponent_Conditional_0_Conditional_0_Template(rf, ctx) { if (rf & 1) {
33
- i0.ɵɵelementStart(0, "div", 1)(1, "div", 2)(2, "div", 3)(3, "span", 4);
34
- i0.ɵɵtext(4, "Initial Render");
35
- i0.ɵɵelementEnd()();
36
- i0.ɵɵelementStart(5, "div", 5);
37
- i0.ɵɵtext(6);
38
- i0.ɵɵelementStart(7, "span");
39
- i0.ɵɵtext(8, "ms");
40
- i0.ɵɵelementEnd()()();
41
- i0.ɵɵelementStart(9, "div", 2)(10, "div", 3)(11, "span", 4);
42
- i0.ɵɵtext(12, "Avg Re-Render");
43
- i0.ɵɵelementEnd()();
44
- i0.ɵɵelementStart(13, "div", 5);
45
- i0.ɵɵtext(14);
46
- i0.ɵɵelementStart(15, "span");
47
- i0.ɵɵtext(16, "ms");
48
- i0.ɵɵelementEnd()();
49
- i0.ɵɵelementStart(17, "div", 6);
50
- i0.ɵɵtext(18);
51
- i0.ɵɵelementEnd()();
52
- i0.ɵɵelementStart(19, "div", 2)(20, "div", 3)(21, "span", 4);
53
- i0.ɵɵtext(22, "Slowest");
54
- i0.ɵɵelementEnd()();
55
- i0.ɵɵelementStart(23, "div", 5);
56
- i0.ɵɵtext(24);
57
- i0.ɵɵelementStart(25, "span");
58
- i0.ɵɵtext(26, "ms");
59
- i0.ɵɵelementEnd()()();
60
- i0.ɵɵelementStart(27, "div", 2)(28, "div", 3)(29, "span", 4);
61
- i0.ɵɵtext(30, "P95");
62
- i0.ɵɵelementEnd()();
63
- i0.ɵɵelementStart(31, "div", 5);
64
- i0.ɵɵtext(32);
65
- i0.ɵɵelementStart(33, "span");
66
- i0.ɵɵtext(34, "ms");
67
- i0.ɵɵelementEnd()()();
68
- i0.ɵɵelementStart(35, "div", 2)(36, "div", 3)(37, "span", 4);
69
- i0.ɵɵtext(38, "Last Render");
70
- i0.ɵɵelementEnd()();
71
- i0.ɵɵelementStart(39, "div", 5);
72
- i0.ɵɵtext(40);
73
- i0.ɵɵelementStart(41, "span");
74
- i0.ɵɵtext(42, "ms");
75
- i0.ɵɵelementEnd()()()();
76
- i0.ɵɵconditionalCreate(43, RenderSectionComponent_Conditional_0_Conditional_0_Conditional_43_Template, 14, 6, "div", 7);
20
+ i0.ɵɵelementStart(0, "div", 1);
21
+ i0.ɵɵelement(1, "prism-metric-card", 2)(2, "prism-metric-card", 3)(3, "prism-metric-card", 4)(4, "prism-metric-card", 5)(5, "prism-metric-card", 6);
22
+ i0.ɵɵelementEnd();
23
+ i0.ɵɵconditionalCreate(6, RenderSectionComponent_Conditional_0_Conditional_0_Conditional_6_Template, 4, 4, "div", 7);
77
24
  } if (rf & 2) {
78
25
  const svc_r1 = i0.ɵɵnextContext();
79
26
  const ctx_r1 = i0.ɵɵnextContext();
80
- i0.ɵɵadvance(5);
81
- i0.ɵɵstyleProp("color", ctx_r1.colorFor(svc_r1.initialRender()));
27
+ const initial_r3 = ctx_r1.statusOf(svc_r1.initialRender());
82
28
  i0.ɵɵadvance();
83
- i0.ɵɵtextInterpolate(ctx_r1.formatMs(svc_r1.initialRender()));
84
- i0.ɵɵadvance(8);
85
- i0.ɵɵtextInterpolate(ctx_r1.formatMs(svc_r1.avgRerender()));
86
- i0.ɵɵadvance(4);
87
- i0.ɵɵtextInterpolate1("", svc_r1.sampleCount(), " samples");
88
- i0.ɵɵadvance(6);
89
- i0.ɵɵtextInterpolate(ctx_r1.formatMs(svc_r1.maxRerender()));
90
- i0.ɵɵadvance(8);
91
- i0.ɵɵtextInterpolate(ctx_r1.formatMs(svc_r1.p95Rerender()));
92
- i0.ɵɵadvance(8);
93
- i0.ɵɵtextInterpolate(ctx_r1.formatMs(svc_r1.lastRerender()));
94
- i0.ɵɵadvance(3);
95
- i0.ɵɵconditional(svc_r1.rerenders().length > 1 ? 43 : -1);
29
+ i0.ɵɵproperty("value", ctx_r1.formatMs(svc_r1.initialRender()))("tone", initial_r3?.tone ?? null)("statusLabel", initial_r3?.label ?? null)("title", ctx_r1.budgetTitle("First render of the component"));
30
+ i0.ɵɵadvance();
31
+ i0.ɵɵproperty("value", ctx_r1.formatMs(svc_r1.avgRerender()))("title", svc_r1.sampleCount() + " samples");
32
+ i0.ɵɵadvance();
33
+ i0.ɵɵproperty("value", ctx_r1.formatMs(svc_r1.maxRerender()));
34
+ i0.ɵɵadvance();
35
+ i0.ɵɵproperty("value", ctx_r1.formatMs(svc_r1.p95Rerender()));
36
+ i0.ɵɵadvance();
37
+ i0.ɵɵproperty("value", ctx_r1.formatMs(svc_r1.lastRerender()));
38
+ i0.ɵɵadvance();
39
+ i0.ɵɵconditional(svc_r1.rerenders().length > 1 ? 6 : -1);
96
40
  } }
97
41
  function RenderSectionComponent_Conditional_0_Conditional_1_Template(rf, ctx) { if (rf & 1) {
98
- i0.ɵɵelementStart(0, "div", 0)(1, "div", 13);
42
+ i0.ɵɵelementStart(0, "div", 0)(1, "span");
99
43
  i0.ɵɵtext(2, "No profiling data yet");
100
44
  i0.ɵɵelementEnd();
101
- i0.ɵɵelementStart(3, "div", 14);
45
+ i0.ɵɵelementStart(3, "span");
102
46
  i0.ɵɵtext(4, "Click Profile to start recording render times");
103
47
  i0.ɵɵelementEnd()();
104
48
  } }
105
49
  function RenderSectionComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
106
- i0.ɵɵconditionalCreate(0, RenderSectionComponent_Conditional_0_Conditional_0_Template, 44, 9)(1, RenderSectionComponent_Conditional_0_Conditional_1_Template, 5, 0, "div", 0);
50
+ i0.ɵɵconditionalCreate(0, RenderSectionComponent_Conditional_0_Conditional_0_Template, 7, 10)(1, RenderSectionComponent_Conditional_0_Conditional_1_Template, 5, 0, "div", 0);
107
51
  } if (rf & 2) {
108
52
  const svc_r1 = ctx;
109
53
  i0.ɵɵconditional(svc_r1.initialRender() !== null || svc_r1.rerenders().length > 0 ? 0 : 1);
@@ -115,41 +59,31 @@ export class RenderSectionComponent {
115
59
  ...(ngDevMode ? [{ debugName: "warnMs" }] : /* istanbul ignore next */ []));
116
60
  critMs = input(16, /* @ts-ignore */
117
61
  ...(ngDevMode ? [{ debugName: "critMs" }] : /* istanbul ignore next */ []));
118
- initialClass = computed(() => {
119
- const v = this.renderService().initialRender();
120
- if (v === null)
121
- return '';
122
- if (v >= this.critMs())
123
- return 'crit';
124
- if (v >= this.warnMs())
125
- return 'warn';
126
- return '';
127
- }, /* @ts-ignore */
128
- ...(ngDevMode ? [{ debugName: "initialClass" }] : /* istanbul ignore next */ []));
129
62
  formatMs(value) {
130
63
  if (value === null)
131
64
  return '—';
132
65
  return value < 10 ? value.toFixed(2) : value.toFixed(1);
133
66
  }
134
- colorFor(value) {
67
+ /** A measured render time graded against the budget; `null` when there is nothing to grade. */
68
+ statusOf(value) {
135
69
  if (value === null)
136
- return '';
137
- if (value >= this.critMs())
138
- return 'var(--prism-danger)';
139
- if (value >= this.warnMs())
140
- return 'var(--prism-warn)';
141
- return 'var(--prism-success)';
70
+ return null;
71
+ const status = perfStatus(value, this.warnMs(), this.critMs());
72
+ return { tone: toneForStatus(status.tone), label: status.label };
73
+ }
74
+ budgetTitle(what) {
75
+ return `${what} · warning above ${this.warnMs()} ms, critical above ${this.critMs()} ms`;
142
76
  }
143
77
  static ɵfac = function RenderSectionComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || RenderSectionComponent)(); };
144
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RenderSectionComponent, selectors: [["perf-render-section"]], inputs: { renderService: [1, "renderService"], warnMs: [1, "warnMs"], critMs: [1, "critMs"] }, decls: 1, vars: 1, consts: [[1, "perf-empty"], [1, "perf-body"], [1, "perf-card"], [1, "perf-card-head"], [1, "perf-card-name"], [1, "perf-card-val"], [1, "perf-card-sub"], [1, "perf-detail"], [1, "perf-card", "perf-card--block"], [1, "perf-sparkline-legend"], [1, "perf-sparkline-hl"], [1, "perf-sparkline-wrap"], [3, "samples", "thresholdWarn", "height"], [1, "perf-empty-text"], [1, "perf-empty-hint"]], template: function RenderSectionComponent_Template(rf, ctx) { if (rf & 1) {
78
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: RenderSectionComponent, selectors: [["perf-render-section"]], inputs: { renderService: [1, "renderService"], warnMs: [1, "warnMs"], critMs: [1, "critMs"] }, decls: 1, vars: 1, consts: [[1, "perf-empty"], [1, "perf-grid"], ["label", "Initial render", "unit", "ms", 3, "value", "tone", "statusLabel", "title"], ["label", "Avg re-render", "unit", "ms", 3, "value", "title"], ["label", "Slowest", "unit", "ms", 3, "value"], ["label", "P95", "unit", "ms", 3, "value"], ["label", "Last render", "unit", "ms", 3, "value"], [1, "perf-block", 3, "title"], [1, "perf-block__label"], [3, "samples", "thresholdWarn", "height"]], template: function RenderSectionComponent_Template(rf, ctx) { if (rf & 1) {
145
79
  i0.ɵɵconditionalCreate(0, RenderSectionComponent_Conditional_0_Template, 2, 1);
146
80
  } if (rf & 2) {
147
81
  let tmp_0_0;
148
82
  i0.ɵɵconditional((tmp_0_0 = ctx.renderService()) ? 0 : -1, tmp_0_0);
149
- } }, dependencies: [SparklineComponent], styles: [".perf-body[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n align-content: start;\n padding: 20px 24px;\n}\n\n.perf-card[_ngcontent-%COMP%] {\n padding: 16px;\n background: var(--%NS%prism-bg-surface);\n border: 1px solid var(--%NS%prism-border);\n border-radius: 10px;\n}\n\n.perf-card--block[_ngcontent-%COMP%] {\n display: block;\n}\n\n.perf-card-head[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n}\n\n.perf-card-name[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 700;\n color: var(--%NS%prism-text-ghost);\n text-transform: uppercase;\n letter-spacing: 0.08em;\n}\n\n.perf-card-val[_ngcontent-%COMP%] {\n display: flex;\n gap: 4px;\n align-items: baseline;\n font-family: 'JetBrains Mono', monospace;\n font-size: 26px;\n font-weight: 700;\n color: var(--%NS%prism-text);\n letter-spacing: -0.02em;\n}\n\n.perf-card-val[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 13px;\n font-weight: 500;\n color: var(--%NS%prism-text-muted);\n}\n\n.perf-card-sub[_ngcontent-%COMP%] {\n margin-top: 4px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n color: var(--%NS%prism-text-muted);\n}\n\n.perf-detail[_ngcontent-%COMP%] {\n padding: 0 24px 20px;\n}\n\n.perf-sparkline-legend[_ngcontent-%COMP%] {\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n color: var(--%NS%prism-text-muted);\n}\n\n.perf-sparkline-hl[_ngcontent-%COMP%] {\n color: var(--%NS%prism-primary);\n}\n\n.perf-sparkline-wrap[_ngcontent-%COMP%] {\n position: relative;\n margin-top: 10px;\n}\n\n.perf-empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n align-items: center;\n justify-content: center;\n height: 100%;\n min-height: 180px;\n}\n\n.perf-empty-text[_ngcontent-%COMP%] {\n font-size: 12px;\n color: var(--%NS%prism-text-ghost);\n}\n\n.perf-empty-hint[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--%NS%prism-text-ghost);\n opacity: 0.6;\n}"] });
83
+ } }, dependencies: [SparklineComponent, PrismMetricCardComponent], styles: ["\n\n\n[_nghost-%COMP%] {\n display: block;\n}\n\n.perf-grid[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));\n gap: 12px;\n padding: 16px 18px 0;\n}\n\n.perf-block[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px 12px;\n margin: 12px 18px 0;\n background: var(--%NS%prism-bg-surface);\n border: 1px solid var(--%NS%prism-border);\n border-radius: 6px;\n}\n\n.perf-block[_ngcontent-%COMP%]:last-child {\n margin-bottom: 16px;\n}\n\n.perf-block__label[_ngcontent-%COMP%] {\n font-size: 11px;\n font-weight: 600;\n color: var(--%NS%prism-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n\n\n.perf-bars[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 84px minmax(0, 1fr) auto;\n gap: 10px;\n align-items: center;\n font-family: var(--%NS%font-mono);\n font-size: 12px;\n}\n\n.perf-bar-label[_ngcontent-%COMP%] {\n color: var(--%NS%prism-text-muted);\n}\n\n.perf-bar-track[_ngcontent-%COMP%] {\n height: 6px;\n overflow: hidden;\n background: var(--%NS%prism-input-bg);\n border-radius: 3px;\n}\n\n.perf-bar-fill[_ngcontent-%COMP%] {\n height: 100%;\n background: var(--%NS%prism-primary);\n border-radius: 3px;\n}\n\n.perf-bar-val[_ngcontent-%COMP%] {\n color: var(--%NS%prism-text-2);\n white-space: nowrap;\n}\n\n.perf-empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n gap: 6px;\n align-items: center;\n justify-content: center;\n height: 100%;\n min-height: 160px;\n font-size: 12.5px;\n color: var(--%NS%prism-text-muted);\n}\n\n.perf-block[_ngcontent-%COMP%] > prism-badge[_ngcontent-%COMP%] {\n align-self: flex-start;\n}"] });
150
84
  }
151
85
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RenderSectionComponent, [{
152
86
  type: Component,
153
- args: [{ selector: 'perf-render-section', standalone: true, imports: [SparklineComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (renderService(); as svc) {\n @if (svc.initialRender() !== null || svc.rerenders().length > 0) {\n <div class=\"perf-body\">\n <div class=\"perf-card\">\n <div class=\"perf-card-head\"><span class=\"perf-card-name\">Initial Render</span></div>\n <div\n class=\"perf-card-val\"\n [style.color]=\"colorFor(svc.initialRender())\"\n >{{ formatMs(svc.initialRender()) }}<span>ms</span></div\n >\n </div>\n <div class=\"perf-card\">\n <div class=\"perf-card-head\"><span class=\"perf-card-name\">Avg Re-Render</span></div>\n <div class=\"perf-card-val\">{{ formatMs(svc.avgRerender()) }}<span>ms</span></div>\n <div class=\"perf-card-sub\">{{ svc.sampleCount() }} samples</div>\n </div>\n <div class=\"perf-card\">\n <div class=\"perf-card-head\"><span class=\"perf-card-name\">Slowest</span></div>\n <div class=\"perf-card-val\">{{ formatMs(svc.maxRerender()) }}<span>ms</span></div>\n </div>\n <div class=\"perf-card\">\n <div class=\"perf-card-head\"><span class=\"perf-card-name\">P95</span></div>\n <div class=\"perf-card-val\">{{ formatMs(svc.p95Rerender()) }}<span>ms</span></div>\n </div>\n <div class=\"perf-card\">\n <div class=\"perf-card-head\"><span class=\"perf-card-name\">Last Render</span></div>\n <div class=\"perf-card-val\">{{ formatMs(svc.lastRerender()) }}<span>ms</span></div>\n </div>\n </div>\n\n @if (svc.rerenders().length > 1) {\n <div class=\"perf-detail\">\n <div class=\"perf-card perf-card--block\">\n <div class=\"perf-card-head\">\n <span class=\"perf-card-name\">Render Time Trace &mdash; Last {{ svc.sampleCount() }} Samples</span>\n <span class=\"perf-sparkline-legend\"\n >avg <span class=\"perf-sparkline-hl\">{{ formatMs(svc.avgRerender()) }} ms</span> &middot; max\n <span class=\"perf-sparkline-hl\">{{ formatMs(svc.maxRerender()) }} ms</span></span\n >\n </div>\n <div class=\"perf-sparkline-wrap\">\n <perf-sparkline\n [samples]=\"svc.rerenders()\"\n [thresholdWarn]=\"warnMs()\"\n [height]=\"80\" />\n </div>\n </div>\n </div>\n }\n } @else {\n <div class=\"perf-empty\">\n <div class=\"perf-empty-text\">No profiling data yet</div>\n <div class=\"perf-empty-hint\">Click Profile to start recording render times</div>\n </div>\n }\n}\n", styles: [".perf-body {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n align-content: start;\n padding: 20px 24px;\n}\n\n.perf-card {\n padding: 16px;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-radius: 10px;\n}\n\n.perf-card--block {\n display: block;\n}\n\n.perf-card-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n}\n\n.perf-card-name {\n font-size: 11px;\n font-weight: 700;\n color: var(--prism-text-ghost);\n text-transform: uppercase;\n letter-spacing: 0.08em;\n}\n\n.perf-card-val {\n display: flex;\n gap: 4px;\n align-items: baseline;\n font-family: 'JetBrains Mono', monospace;\n font-size: 26px;\n font-weight: 700;\n color: var(--prism-text);\n letter-spacing: -0.02em;\n}\n\n.perf-card-val span {\n font-size: 13px;\n font-weight: 500;\n color: var(--prism-text-muted);\n}\n\n.perf-card-sub {\n margin-top: 4px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n color: var(--prism-text-muted);\n}\n\n.perf-detail {\n padding: 0 24px 20px;\n}\n\n.perf-sparkline-legend {\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n color: var(--prism-text-muted);\n}\n\n.perf-sparkline-hl {\n color: var(--prism-primary);\n}\n\n.perf-sparkline-wrap {\n position: relative;\n margin-top: 10px;\n}\n\n.perf-empty {\n display: flex;\n flex-direction: column;\n gap: 8px;\n align-items: center;\n justify-content: center;\n height: 100%;\n min-height: 180px;\n}\n\n.perf-empty-text {\n font-size: 12px;\n color: var(--prism-text-ghost);\n}\n\n.perf-empty-hint {\n font-size: 11px;\n color: var(--prism-text-ghost);\n opacity: 0.6;\n}\n"] }]
87
+ args: [{ selector: 'perf-render-section', standalone: true, imports: [SparklineComponent, PrismMetricCardComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (renderService(); as svc) {\n @if (svc.initialRender() !== null || svc.rerenders().length > 0) {\n <div class=\"perf-grid\">\n @let initial = statusOf(svc.initialRender());\n <prism-metric-card\n label=\"Initial render\"\n unit=\"ms\"\n [value]=\"formatMs(svc.initialRender())\"\n [tone]=\"initial?.tone ?? null\"\n [statusLabel]=\"initial?.label ?? null\"\n [title]=\"budgetTitle('First render of the component')\" />\n <prism-metric-card\n label=\"Avg re-render\"\n unit=\"ms\"\n [value]=\"formatMs(svc.avgRerender())\"\n [title]=\"svc.sampleCount() + ' samples'\" />\n <prism-metric-card\n label=\"Slowest\"\n unit=\"ms\"\n [value]=\"formatMs(svc.maxRerender())\" />\n <prism-metric-card\n label=\"P95\"\n unit=\"ms\"\n [value]=\"formatMs(svc.p95Rerender())\" />\n <prism-metric-card\n label=\"Last render\"\n unit=\"ms\"\n [value]=\"formatMs(svc.lastRerender())\" />\n </div>\n\n @if (svc.rerenders().length > 1) {\n <div\n class=\"perf-block\"\n [title]=\"\n 'Last ' +\n svc.sampleCount() +\n ' samples \u00B7 avg ' +\n formatMs(svc.avgRerender()) +\n ' ms \u00B7 max ' +\n formatMs(svc.maxRerender()) +\n ' ms \u00B7 line at ' +\n warnMs() +\n ' ms'\n \">\n <span class=\"perf-block__label\">Render time trace</span>\n <perf-sparkline\n [samples]=\"svc.rerenders()\"\n [thresholdWarn]=\"warnMs()\"\n [height]=\"80\" />\n </div>\n }\n } @else {\n <div class=\"perf-empty\">\n <span>No profiling data yet</span>\n <span>Click Profile to start recording render times</span>\n </div>\n }\n}\n", styles: ["/* Shared by the bundle, render and memory sections: metric cards on a 12 px\n grid, block cards for bars and traces, and the empty state. The cards\n themselves are prism-metric-card from @ng-prism/core. */\n:host {\n display: block;\n}\n\n.perf-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));\n gap: 12px;\n padding: 16px 18px 0;\n}\n\n.perf-block {\n display: flex;\n flex-direction: column;\n gap: 8px;\n padding: 10px 12px;\n margin: 12px 18px 0;\n background: var(--prism-bg-surface);\n border: 1px solid var(--prism-border);\n border-radius: 6px;\n}\n\n.perf-block:last-child {\n margin-bottom: 16px;\n}\n\n.perf-block__label {\n font-size: 11px;\n font-weight: 600;\n color: var(--prism-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.06em;\n}\n\n/* Quantities, not verdicts: one colour, the primary, never the brand gradient. */\n.perf-bars {\n display: grid;\n grid-template-columns: 84px minmax(0, 1fr) auto;\n gap: 10px;\n align-items: center;\n font-family: var(--font-mono);\n font-size: 12px;\n}\n\n.perf-bar-label {\n color: var(--prism-text-muted);\n}\n\n.perf-bar-track {\n height: 6px;\n overflow: hidden;\n background: var(--prism-input-bg);\n border-radius: 3px;\n}\n\n.perf-bar-fill {\n height: 100%;\n background: var(--prism-primary);\n border-radius: 3px;\n}\n\n.perf-bar-val {\n color: var(--prism-text-2);\n white-space: nowrap;\n}\n\n.perf-empty {\n display: flex;\n flex-direction: column;\n gap: 6px;\n align-items: center;\n justify-content: center;\n height: 100%;\n min-height: 160px;\n font-size: 12.5px;\n color: var(--prism-text-muted);\n}\n\n.perf-block > prism-badge {\n align-self: flex-start;\n}\n"] }]
154
88
  }], null, { renderService: [{ type: i0.Input, args: [{ isSignal: true, alias: "renderService", required: true }] }], warnMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "warnMs", required: false }] }], critMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "critMs", required: false }] }] }); })();
155
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RenderSectionComponent, { className: "RenderSectionComponent", filePath: "render/render-section.component.ts", lineNumber: 13 }); })();
89
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RenderSectionComponent, { className: "RenderSectionComponent", filePath: "render/render-section.component.ts", lineNumber: 15 }); })();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-prism/plugin-perf",
3
- "version": "22.5.0",
3
+ "version": "22.7.0-beta.0",
4
4
  "description": "Performance profiling panel for ng-prism: bundle size, render time, memory footprint.",
5
5
  "ng-add": {
6
6
  "save": "devDependencies"
@@ -36,7 +36,7 @@
36
36
  "peerDependencies": {
37
37
  "@angular/core": ">=20.0.0",
38
38
  "typescript": ">=5.0.0",
39
- "@ng-prism/core": "^22.5.0-0",
39
+ "@ng-prism/core": "^22.7.0-0",
40
40
  "@angular-devkit/schematics": ">=20.0.0"
41
41
  },
42
42
  "devDependencies": {