@ng-prism/plugin-perf 22.2.0 → 22.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +6 -6
  2. package/dist/bundle/bundle-scanner.d.ts.map +1 -1
  3. package/dist/bundle/bundle-scanner.js +11 -7
  4. package/dist/bundle/bundle-section.component.d.ts +4 -4
  5. package/dist/bundle/bundle-section.component.d.ts.map +1 -1
  6. package/dist/bundle/bundle-section.component.js +3 -72
  7. package/dist/index.browser.d.ts +1 -1
  8. package/dist/index.browser.d.ts.map +1 -1
  9. package/dist/index.d.ts +1 -1
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/memory/memory-section.component.d.ts +1 -1
  12. package/dist/memory/memory-section.component.d.ts.map +1 -1
  13. package/dist/memory/memory-section.component.js +4 -78
  14. package/dist/memory/perf-memory.service.d.ts +1 -1
  15. package/dist/memory/perf-memory.service.d.ts.map +1 -1
  16. package/dist/memory/perf-memory.service.js +2 -5
  17. package/dist/perf-panel.component.d.ts +1 -1
  18. package/dist/perf-panel.component.d.ts.map +1 -1
  19. package/dist/perf-panel.component.js +7 -92
  20. package/dist/perf-plugin.browser.d.ts.map +1 -1
  21. package/dist/perf-plugin.browser.js +3 -3
  22. package/dist/perf-plugin.d.ts.map +1 -1
  23. package/dist/perf-plugin.js +6 -6
  24. package/dist/perf.types.d.ts.map +1 -1
  25. package/dist/perf.types.js +1 -1
  26. package/dist/render/perf-render.service.d.ts.map +1 -1
  27. package/dist/render/perf-render.service.js +1 -1
  28. package/dist/render/render-section.component.d.ts +1 -1
  29. package/dist/render/render-section.component.d.ts.map +1 -1
  30. package/dist/render/render-section.component.js +3 -50
  31. package/dist/render/sparkline.component.d.ts.map +1 -1
  32. package/dist/render/sparkline.component.js +7 -44
  33. package/dist/schematics/ng-add/index.d.ts.map +1 -1
  34. package/dist/schematics/ng-add/index.js +2 -2
  35. package/dist/schematics/ng-add/schema.d.ts.map +1 -1
  36. package/package.json +3 -2
  37. package/schematics/collection.json +7 -7
  38. package/schematics/ng-add/schema.json +14 -14
package/README.md CHANGED
@@ -12,10 +12,10 @@ npm install @ng-prism/plugin-perf
12
12
 
13
13
  ### Peer Dependencies
14
14
 
15
- | Package | Version |
16
- |---|---|
15
+ | Package | Version |
16
+ | ---------------- | ---------- |
17
17
  | `@ng-prism/core` | `>=21.0.0` |
18
- | `@angular/core` | `>=20.0.0` |
18
+ | `@angular/core` | `>=20.0.0` |
19
19
 
20
20
  ## Setup
21
21
 
@@ -25,7 +25,7 @@ import { defineConfig } from '@ng-prism/core/config';
25
25
  import { perfPlugin } from '@ng-prism/plugin-perf';
26
26
 
27
27
  export default defineConfig({
28
- plugins: [perfPlugin()],
28
+ plugins: [perfPlugin()]
29
29
  });
30
30
  ```
31
31
 
@@ -33,8 +33,8 @@ export default defineConfig({
33
33
 
34
34
  - Adds a **Performance** view tab to the styleguide
35
35
  - Profiles component lifecycle:
36
- - Initial render time (`prism:render`)
37
- - Re-render time on input changes (`prism:rerender`)
36
+ - Initial render time (`prism:render`)
37
+ - Re-render time on input changes (`prism:rerender`)
38
38
  - Uses the browser's native Performance API (`performance.mark` / `performance.measure`)
39
39
  - Displays timing metrics per variant
40
40
  - Memory usage tracking
@@ -1 +1 @@
1
- {"version":3,"file":"bundle-scanner.d.ts","sourceRoot":"","sources":["../../src/bundle/bundle-scanner.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,aAAa,CAkB5E"}
1
+ {"version":3,"file":"bundle-scanner.d.ts","sourceRoot":"","sources":["../../src/bundle/bundle-scanner.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,SAAI,GAAG,aAAa,CAmB5E"}
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from 'fs';
2
+ import { dirname, extname, resolve } from 'path';
2
3
  import { gzipSync } from 'zlib';
3
- import { dirname, resolve, extname } from 'path';
4
4
  import ts from 'typescript';
5
5
  export function scanBundle(filePath, maxTreeDepth = 5) {
6
6
  const source = readFileSync(filePath, 'utf-8');
@@ -21,9 +21,7 @@ export function scanBundle(filePath, maxTreeDepth = 5) {
21
21
  function extractImports(sourceFile) {
22
22
  const imports = [];
23
23
  ts.forEachChild(sourceFile, (node) => {
24
- if (ts.isImportDeclaration(node) &&
25
- node.moduleSpecifier &&
26
- ts.isStringLiteral(node.moduleSpecifier)) {
24
+ if (ts.isImportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
27
25
  imports.push(node.moduleSpecifier.text);
28
26
  }
29
27
  });
@@ -64,7 +62,9 @@ function resolveImport(dir, importPath) {
64
62
  readFileSync(withoutExt + ext);
65
63
  return withoutExt + ext;
66
64
  }
67
- catch { /* noop */ }
65
+ catch {
66
+ /* noop */
67
+ }
68
68
  }
69
69
  }
70
70
  for (const ext of extensions) {
@@ -72,14 +72,18 @@ function resolveImport(dir, importPath) {
72
72
  readFileSync(base + ext);
73
73
  return base + ext;
74
74
  }
75
- catch { /* noop */ }
75
+ catch {
76
+ /* noop */
77
+ }
76
78
  }
77
79
  for (const ext of extensions) {
78
80
  try {
79
81
  readFileSync(resolve(base, 'index' + ext));
80
82
  return resolve(base, 'index' + ext);
81
83
  }
82
- catch { /* noop */ }
84
+ catch {
85
+ /* noop */
86
+ }
83
87
  }
84
88
  return null;
85
89
  }
@@ -7,10 +7,10 @@ export declare class BundleSectionComponent {
7
7
  readonly gzipPercent: import("@angular/core").Signal<number>;
8
8
  readonly sourceBarPercent: import("@angular/core").Signal<number>;
9
9
  readonly gzipBarPercent: import("@angular/core").Signal<number>;
10
- readonly sizeStatus: import("@angular/core").Signal<"warn" | "" | "crit" | "good">;
11
- readonly sizeStatusText: import("@angular/core").Signal<"warn" | "" | "crit" | "good">;
12
- readonly gzipStatus: import("@angular/core").Signal<"warn" | "" | "crit" | "good">;
13
- readonly gzipStatusText: import("@angular/core").Signal<"warn" | "" | "crit" | "good">;
10
+ readonly sizeStatus: import("@angular/core").Signal<"" | "crit" | "warn" | "good">;
11
+ readonly sizeStatusText: import("@angular/core").Signal<"" | "crit" | "warn" | "good">;
12
+ readonly gzipStatus: import("@angular/core").Signal<"" | "crit" | "warn" | "good">;
13
+ readonly gzipStatusText: import("@angular/core").Signal<"" | "crit" | "warn" | "good">;
14
14
  readonly visibleImports: import("@angular/core").Signal<string[]>;
15
15
  readonly hiddenImportCount: import("@angular/core").Signal<number>;
16
16
  formatBytes(bytes: number): string;
@@ -1 +1 @@
1
- {"version":3,"file":"bundle-section.component.d.ts","sourceRoot":"","sources":["../../src/bundle/bundle-section.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;;AAEtD,qBAsNa,sBAAsB;IACjC,QAAQ,CAAC,OAAO,4DAAqC;IACrD,QAAQ,CAAC,MAAM,8CAAa;IAC5B,QAAQ,CAAC,MAAM,8CAAa;IAE5B,QAAQ,CAAC,WAAW,yCAIjB;IAEH,QAAQ,CAAC,gBAAgB,yCAKtB;IAEH,QAAQ,CAAC,cAAc,yCAKpB;IAEH,QAAQ,CAAC,UAAU,gEAOhB;IAEH,QAAQ,CAAC,cAAc,gEAMpB;IAEH,QAAQ,CAAC,UAAU,gEAOhB;IAEH,QAAQ,CAAC,cAAc,gEAMpB;IAEH,QAAQ,CAAC,cAAc,2CAGpB;IAEH,QAAQ,CAAC,iBAAiB,yCAIvB;IAEH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIlC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAK/B,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;yCA/EnB,sBAAsB;2CAAtB,sBAAsB;CAmFlC"}
1
+ {"version":3,"file":"bundle-section.component.d.ts","sourceRoot":"","sources":["../../src/bundle/bundle-section.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;;AAEtD,qBAOa,sBAAsB;IAC/B,QAAQ,CAAC,OAAO,4DAAqC;IACrD,QAAQ,CAAC,MAAM,8CAAa;IAC5B,QAAQ,CAAC,MAAM,8CAAa;IAE5B,QAAQ,CAAC,WAAW,yCAKjB;IAEH,QAAQ,CAAC,gBAAgB,yCAOtB;IAEH,QAAQ,CAAC,cAAc,yCAOpB;IAEH,QAAQ,CAAC,UAAU,gEAShB;IAEH,QAAQ,CAAC,cAAc,gEAOpB;IAEH,QAAQ,CAAC,UAAU,gEAShB;IAEH,QAAQ,CAAC,cAAc,gEAOpB;IAEH,QAAQ,CAAC,cAAc,2CAIpB;IAEH,QAAQ,CAAC,iBAAiB,yCAKvB;IAEH,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAIlC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAM/B,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;yCA7FrB,sBAAsB;2CAAtB,sBAAsB;CAiGlC"}
@@ -262,79 +262,10 @@ export class BundleSectionComponent {
262
262
  } if (rf & 2) {
263
263
  let tmp_0_0;
264
264
  i0.ɵɵconditional((tmp_0_0 = ctx.metrics()) ? 0 : 1, tmp_0_0);
265
- } }, styles: [".perf-body[_ngcontent-%COMP%] {\n padding: 20px 24px;\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n align-content: start;\n }\n .perf-body--4col[_ngcontent-%COMP%] { grid-template-columns: repeat(4, 1fr); }\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 .perf-card--block[_ngcontent-%COMP%] { display: block; }\n .perf-card-head[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n }\n .perf-card-name[_ngcontent-%COMP%] {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--%NS%prism-text-ghost);\n font-weight: 700;\n }\n .perf-card-status[_ngcontent-%COMP%] {\n font-size: 10px;\n padding: 2px 6px;\n border-radius: 3px;\n font-weight: 700;\n letter-spacing: 0.04em;\n }\n .perf-card-status.good[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--%NS%prism-success) 15%, transparent);\n color: var(--%NS%prism-success);\n }\n .perf-card-status.warn[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--%NS%prism-warn) 15%, transparent);\n color: var(--%NS%prism-warn);\n }\n .perf-card-status.crit[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--%NS%prism-danger) 15%, transparent);\n color: var(--%NS%prism-danger);\n }\n .perf-card-val[_ngcontent-%COMP%] {\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 display: flex;\n align-items: baseline;\n gap: 4px;\n }\n .perf-card-val[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--%NS%prism-text-muted);\n font-weight: 500;\n }\n .perf-card-sub[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--%NS%prism-text-muted);\n margin-top: 4px;\n font-family: 'JetBrains Mono', monospace;\n }\n\n .perf-detail[_ngcontent-%COMP%] { padding: 0 24px 16px; }\n .perf-detail--last[_ngcontent-%COMP%] { padding-bottom: 20px; }\n\n .perf-bars[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 80px 1fr auto;\n gap: 10px;\n align-items: center;\n font-family: 'JetBrains Mono', monospace;\n font-size: 12px;\n margin-top: 8px;\n }\n .perf-bar-label[_ngcontent-%COMP%] { color: var(--%NS%prism-text-muted); }\n .perf-bar-track[_ngcontent-%COMP%] {\n height: 8px;\n background: var(--%NS%prism-input-bg);\n border-radius: 4px;\n overflow: hidden;\n }\n .perf-bar-fill[_ngcontent-%COMP%] {\n height: 100%;\n background: linear-gradient(90deg, var(--%NS%prism-primary-from), var(--%NS%prism-primary-to));\n border-radius: 4px;\n }\n .perf-bar-val[_ngcontent-%COMP%] { color: var(--%NS%prism-primary); }\n\n .perf-thresholds[_ngcontent-%COMP%] {\n margin-top: 10px;\n font-size: 11px;\n color: var(--%NS%prism-text-muted);\n font-family: 'JetBrains Mono', monospace;\n }\n .perf-thresholds--warn[_ngcontent-%COMP%] { color: var(--%NS%prism-warn); }\n .perf-thresholds--crit[_ngcontent-%COMP%] { color: var(--%NS%prism-danger); }\n\n .perf-chips[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-top: 10px;\n }\n .perf-chip[_ngcontent-%COMP%] {\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n padding: 4px 10px;\n background: var(--%NS%prism-input-bg);\n color: var(--%NS%prism-text-2);\n border: 1px solid var(--%NS%prism-border);\n border-radius: 5px;\n }\n .perf-chip.angular[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--%NS%prism-primary) 10%, transparent);\n color: var(--%NS%prism-primary);\n border-color: color-mix(in srgb, var(--%NS%prism-primary) 25%, transparent);\n }\n\n .perf-empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n height: 100%;\n min-height: 180px;\n }\n .perf-empty-text[_ngcontent-%COMP%] { font-size: 12px; color: var(--%NS%prism-text-ghost); }\n .perf-empty-hint[_ngcontent-%COMP%] { font-size: 11px; color: var(--%NS%prism-text-ghost); opacity: 0.6; }"] });
265
+ } }, 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-body--4col[_ngcontent-%COMP%] {\n grid-template-columns: repeat(4, 1fr);\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-status[_ngcontent-%COMP%] {\n padding: 2px 6px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.04em;\n border-radius: 3px;\n}\n\n.perf-card-status.good[_ngcontent-%COMP%] {\n color: var(--%NS%prism-success);\n background: color-mix(in srgb, var(--%NS%prism-success) 15%, transparent);\n}\n\n.perf-card-status.warn[_ngcontent-%COMP%] {\n color: var(--%NS%prism-warn);\n background: color-mix(in srgb, var(--%NS%prism-warn) 15%, transparent);\n}\n\n.perf-card-status.crit[_ngcontent-%COMP%] {\n color: var(--%NS%prism-danger);\n background: color-mix(in srgb, var(--%NS%prism-danger) 15%, transparent);\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 16px;\n}\n\n.perf-detail--last[_ngcontent-%COMP%] {\n padding-bottom: 20px;\n}\n\n.perf-bars[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 80px 1fr auto;\n gap: 10px;\n align-items: center;\n margin-top: 8px;\n font-family: 'JetBrains Mono', monospace;\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: 8px;\n overflow: hidden;\n background: var(--%NS%prism-input-bg);\n border-radius: 4px;\n}\n\n.perf-bar-fill[_ngcontent-%COMP%] {\n height: 100%;\n background: linear-gradient(90deg, var(--%NS%prism-primary-from), var(--%NS%prism-primary-to));\n border-radius: 4px;\n}\n\n.perf-bar-val[_ngcontent-%COMP%] {\n color: var(--%NS%prism-primary);\n}\n\n.perf-thresholds[_ngcontent-%COMP%] {\n margin-top: 10px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n color: var(--%NS%prism-text-muted);\n}\n\n.perf-thresholds--warn[_ngcontent-%COMP%] {\n color: var(--%NS%prism-warn);\n}\n\n.perf-thresholds--crit[_ngcontent-%COMP%] {\n color: var(--%NS%prism-danger);\n}\n\n.perf-chips[_ngcontent-%COMP%] {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-top: 10px;\n}\n\n.perf-chip[_ngcontent-%COMP%] {\n padding: 4px 10px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n color: var(--%NS%prism-text-2);\n background: var(--%NS%prism-input-bg);\n border: 1px solid var(--%NS%prism-border);\n border-radius: 5px;\n}\n\n.perf-chip.angular[_ngcontent-%COMP%] {\n color: var(--%NS%prism-primary);\n background: color-mix(in srgb, var(--%NS%prism-primary) 10%, transparent);\n border-color: color-mix(in srgb, var(--%NS%prism-primary) 25%, transparent);\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}"] });
266
266
  }
267
267
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(BundleSectionComponent, [{
268
268
  type: Component,
269
- args: [{ selector: 'perf-bundle-section', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
270
- @if (metrics(); as m) {
271
- <div class="perf-body perf-body--4col">
272
- <div class="perf-card">
273
- <div class="perf-card-head">
274
- <span class="perf-card-name">Source Size</span>
275
- <span class="perf-card-status" [class]="sizeStatus()">{{ sizeStatusText() }}</span>
276
- </div>
277
- <div class="perf-card-val">{{ formatKb(m.sourceSize) }}<span>kb</span></div>
278
- <div class="perf-card-sub">{{ formatBytes(m.sourceSize) }} bytes</div>
279
- </div>
280
- <div class="perf-card">
281
- <div class="perf-card-head">
282
- <span class="perf-card-name">Gzip Estimate</span>
283
- <span class="perf-card-status" [class]="gzipStatus()">{{ gzipStatusText() }}</span>
284
- </div>
285
- <div class="perf-card-val">{{ formatKb(m.gzipEstimate) }}<span>kb</span></div>
286
- <div class="perf-card-sub">~{{ gzipPercent() }}% of source</div>
287
- </div>
288
- <div class="perf-card">
289
- <div class="perf-card-head">
290
- <span class="perf-card-name">Direct Imports</span>
291
- </div>
292
- <div class="perf-card-val">{{ m.directImports }}</div>
293
- <div class="perf-card-sub">top-level only</div>
294
- </div>
295
- <div class="perf-card">
296
- <div class="perf-card-head">
297
- <span class="perf-card-name">Tree Depth</span>
298
- </div>
299
- <div class="perf-card-val">{{ m.treeDepth }}<span>/ 10</span></div>
300
- <div class="perf-card-sub">import levels</div>
301
- </div>
302
- </div>
303
-
304
- <div class="perf-detail">
305
- <div class="perf-card perf-card--block">
306
- <div class="perf-card-head"><span class="perf-card-name">Size Breakdown</span></div>
307
- <div class="perf-bars">
308
- <span class="perf-bar-label">source</span>
309
- <div class="perf-bar-track"><div class="perf-bar-fill" [style.width.%]="sourceBarPercent()"></div></div>
310
- <span class="perf-bar-val">{{ formatKb(m.sourceSize) }} KB</span>
311
- <span class="perf-bar-label">gzip est.</span>
312
- <div class="perf-bar-track"><div class="perf-bar-fill" [style.width.%]="gzipBarPercent()"></div></div>
313
- <span class="perf-bar-val">{{ formatKb(m.gzipEstimate) }} KB</span>
314
- </div>
315
- <div class="perf-thresholds">Thresholds: <span class="perf-thresholds--warn">warn &ge; {{ warnKb() }} KB</span> &middot; <span class="perf-thresholds--crit">crit &ge; {{ critKb() }} KB</span></div>
316
- </div>
317
- </div>
318
-
319
- <div class="perf-detail perf-detail--last">
320
- <div class="perf-card perf-card--block">
321
- <div class="perf-card-head"><span class="perf-card-name">Direct Imports ({{ m.directImports }})</span></div>
322
- <div class="perf-chips">
323
- @for (imp of visibleImports(); track imp) {
324
- <span class="perf-chip" [class]="chipClass(imp)">{{ imp }}</span>
325
- }
326
- @if (hiddenImportCount() > 0) {
327
- <span class="perf-chip">+{{ hiddenImportCount() }} more</span>
328
- }
329
- </div>
330
- </div>
331
- </div>
332
- } @else {
333
- <div class="perf-empty">
334
- <div class="perf-empty-text">No bundle data available</div>
335
- <div class="perf-empty-hint">Bundle metrics are collected at build time</div>
336
- </div>
337
- }
338
- `, styles: ["\n .perf-body {\n padding: 20px 24px;\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n align-content: start;\n }\n .perf-body--4col { grid-template-columns: repeat(4, 1fr); }\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 .perf-card--block { display: block; }\n .perf-card-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n }\n .perf-card-name {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--prism-text-ghost);\n font-weight: 700;\n }\n .perf-card-status {\n font-size: 10px;\n padding: 2px 6px;\n border-radius: 3px;\n font-weight: 700;\n letter-spacing: 0.04em;\n }\n .perf-card-status.good {\n background: color-mix(in srgb, var(--prism-success) 15%, transparent);\n color: var(--prism-success);\n }\n .perf-card-status.warn {\n background: color-mix(in srgb, var(--prism-warn) 15%, transparent);\n color: var(--prism-warn);\n }\n .perf-card-status.crit {\n background: color-mix(in srgb, var(--prism-danger) 15%, transparent);\n color: var(--prism-danger);\n }\n .perf-card-val {\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 display: flex;\n align-items: baseline;\n gap: 4px;\n }\n .perf-card-val span {\n font-size: 13px;\n color: var(--prism-text-muted);\n font-weight: 500;\n }\n .perf-card-sub {\n font-size: 11px;\n color: var(--prism-text-muted);\n margin-top: 4px;\n font-family: 'JetBrains Mono', monospace;\n }\n\n .perf-detail { padding: 0 24px 16px; }\n .perf-detail--last { padding-bottom: 20px; }\n\n .perf-bars {\n display: grid;\n grid-template-columns: 80px 1fr auto;\n gap: 10px;\n align-items: center;\n font-family: 'JetBrains Mono', monospace;\n font-size: 12px;\n margin-top: 8px;\n }\n .perf-bar-label { color: var(--prism-text-muted); }\n .perf-bar-track {\n height: 8px;\n background: var(--prism-input-bg);\n border-radius: 4px;\n overflow: hidden;\n }\n .perf-bar-fill {\n height: 100%;\n background: linear-gradient(90deg, var(--prism-primary-from), var(--prism-primary-to));\n border-radius: 4px;\n }\n .perf-bar-val { color: var(--prism-primary); }\n\n .perf-thresholds {\n margin-top: 10px;\n font-size: 11px;\n color: var(--prism-text-muted);\n font-family: 'JetBrains Mono', monospace;\n }\n .perf-thresholds--warn { color: var(--prism-warn); }\n .perf-thresholds--crit { color: var(--prism-danger); }\n\n .perf-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-top: 10px;\n }\n .perf-chip {\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n padding: 4px 10px;\n background: var(--prism-input-bg);\n color: var(--prism-text-2);\n border: 1px solid var(--prism-border);\n border-radius: 5px;\n }\n .perf-chip.angular {\n background: color-mix(in srgb, var(--prism-primary) 10%, transparent);\n color: var(--prism-primary);\n border-color: color-mix(in srgb, var(--prism-primary) 25%, transparent);\n }\n\n .perf-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n height: 100%;\n min-height: 180px;\n }\n .perf-empty-text { font-size: 12px; color: var(--prism-text-ghost); }\n .perf-empty-hint { font-size: 11px; color: var(--prism-text-ghost); opacity: 0.6; }\n "] }]
269
+ args: [{ selector: 'perf-bundle-section', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (metrics(); as m) {\n <div class=\"perf-body perf-body--4col\">\n <div class=\"perf-card\">\n <div class=\"perf-card-head\">\n <span class=\"perf-card-name\">Source Size</span>\n <span\n class=\"perf-card-status\"\n [class]=\"sizeStatus()\"\n >{{ sizeStatusText() }}</span\n >\n </div>\n <div class=\"perf-card-val\">{{ formatKb(m.sourceSize) }}<span>kb</span></div>\n <div class=\"perf-card-sub\">{{ formatBytes(m.sourceSize) }} bytes</div>\n </div>\n <div class=\"perf-card\">\n <div class=\"perf-card-head\">\n <span class=\"perf-card-name\">Gzip Estimate</span>\n <span\n class=\"perf-card-status\"\n [class]=\"gzipStatus()\"\n >{{ gzipStatusText() }}</span\n >\n </div>\n <div class=\"perf-card-val\">{{ formatKb(m.gzipEstimate) }}<span>kb</span></div>\n <div class=\"perf-card-sub\">~{{ gzipPercent() }}% of source</div>\n </div>\n <div class=\"perf-card\">\n <div class=\"perf-card-head\">\n <span class=\"perf-card-name\">Direct Imports</span>\n </div>\n <div class=\"perf-card-val\">{{ m.directImports }}</div>\n <div class=\"perf-card-sub\">top-level only</div>\n </div>\n <div class=\"perf-card\">\n <div class=\"perf-card-head\">\n <span class=\"perf-card-name\">Tree Depth</span>\n </div>\n <div class=\"perf-card-val\">{{ m.treeDepth }}<span>/ 10</span></div>\n <div class=\"perf-card-sub\">import levels</div>\n </div>\n </div>\n\n <div class=\"perf-detail\">\n <div class=\"perf-card perf-card--block\">\n <div class=\"perf-card-head\"><span class=\"perf-card-name\">Size Breakdown</span></div>\n <div class=\"perf-bars\">\n <span class=\"perf-bar-label\">source</span>\n <div class=\"perf-bar-track\"\n ><div\n class=\"perf-bar-fill\"\n [style.width.%]=\"sourceBarPercent()\"></div\n ></div>\n <span class=\"perf-bar-val\">{{ formatKb(m.sourceSize) }} KB</span>\n <span class=\"perf-bar-label\">gzip est.</span>\n <div class=\"perf-bar-track\"\n ><div\n class=\"perf-bar-fill\"\n [style.width.%]=\"gzipBarPercent()\"></div\n ></div>\n <span class=\"perf-bar-val\">{{ formatKb(m.gzipEstimate) }} KB</span>\n </div>\n <div class=\"perf-thresholds\"\n >Thresholds: <span class=\"perf-thresholds--warn\">warn &ge; {{ warnKb() }} KB</span> &middot;\n <span class=\"perf-thresholds--crit\">crit &ge; {{ critKb() }} KB</span></div\n >\n </div>\n </div>\n\n <div class=\"perf-detail perf-detail--last\">\n <div class=\"perf-card perf-card--block\">\n <div class=\"perf-card-head\"\n ><span class=\"perf-card-name\">Direct Imports ({{ m.directImports }})</span></div\n >\n <div class=\"perf-chips\">\n @for (imp of visibleImports(); track imp) {\n <span\n class=\"perf-chip\"\n [class]=\"chipClass(imp)\"\n >{{ imp }}</span\n >\n }\n @if (hiddenImportCount() > 0) {\n <span class=\"perf-chip\">+{{ hiddenImportCount() }} more</span>\n }\n </div>\n </div>\n </div>\n} @else {\n <div class=\"perf-empty\">\n <div class=\"perf-empty-text\">No bundle data available</div>\n <div class=\"perf-empty-hint\">Bundle metrics are collected at build time</div>\n </div>\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-body--4col {\n grid-template-columns: repeat(4, 1fr);\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-status {\n padding: 2px 6px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.04em;\n border-radius: 3px;\n}\n\n.perf-card-status.good {\n color: var(--prism-success);\n background: color-mix(in srgb, var(--prism-success) 15%, transparent);\n}\n\n.perf-card-status.warn {\n color: var(--prism-warn);\n background: color-mix(in srgb, var(--prism-warn) 15%, transparent);\n}\n\n.perf-card-status.crit {\n color: var(--prism-danger);\n background: color-mix(in srgb, var(--prism-danger) 15%, transparent);\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 16px;\n}\n\n.perf-detail--last {\n padding-bottom: 20px;\n}\n\n.perf-bars {\n display: grid;\n grid-template-columns: 80px 1fr auto;\n gap: 10px;\n align-items: center;\n margin-top: 8px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 12px;\n}\n\n.perf-bar-label {\n color: var(--prism-text-muted);\n}\n\n.perf-bar-track {\n height: 8px;\n overflow: hidden;\n background: var(--prism-input-bg);\n border-radius: 4px;\n}\n\n.perf-bar-fill {\n height: 100%;\n background: linear-gradient(90deg, var(--prism-primary-from), var(--prism-primary-to));\n border-radius: 4px;\n}\n\n.perf-bar-val {\n color: var(--prism-primary);\n}\n\n.perf-thresholds {\n margin-top: 10px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n color: var(--prism-text-muted);\n}\n\n.perf-thresholds--warn {\n color: var(--prism-warn);\n}\n\n.perf-thresholds--crit {\n color: var(--prism-danger);\n}\n\n.perf-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n margin-top: 10px;\n}\n\n.perf-chip {\n padding: 4px 10px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n color: var(--prism-text-2);\n background: var(--prism-input-bg);\n border: 1px solid var(--prism-border);\n border-radius: 5px;\n}\n\n.perf-chip.angular {\n color: var(--prism-primary);\n background: color-mix(in srgb, var(--prism-primary) 10%, transparent);\n border-color: color-mix(in srgb, var(--prism-primary) 25%, transparent);\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"] }]
339
270
  }], null, { metrics: [{ type: i0.Input, args: [{ isSignal: true, alias: "metrics", required: false }] }], warnKb: [{ type: i0.Input, args: [{ isSignal: true, alias: "warnKb", required: false }] }], critKb: [{ type: i0.Input, args: [{ isSignal: true, alias: "critKb", required: false }] }] }); })();
340
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(BundleSectionComponent, { className: "BundleSectionComponent", filePath: "bundle/bundle-section.component.ts", lineNumber: 218 }); })();
271
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(BundleSectionComponent, { className: "BundleSectionComponent", filePath: "bundle/bundle-section.component.ts", lineNumber: 11 }); })();
@@ -1,5 +1,5 @@
1
1
  export { perfPlugin } from './perf-plugin.browser.js';
2
2
  export { PerfPanelComponent } from './perf-panel.component.js';
3
3
  export { PERF_PLUGIN_CONFIG } from './perf-config.token.js';
4
- export type { PerfPluginOptions, PerfThresholds, BundleMetrics } from './perf.types.js';
4
+ export type { BundleMetrics, PerfPluginOptions, PerfThresholds } from './perf.types.js';
5
5
  //# sourceMappingURL=index.browser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.browser.d.ts","sourceRoot":"","sources":["../src/index.browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.browser.d.ts","sourceRoot":"","sources":["../src/index.browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { perfPlugin } from './perf-plugin.js';
2
2
  export { PerfPanelComponent } from './perf-panel.component.js';
3
3
  export { PERF_PLUGIN_CONFIG } from './perf-config.token.js';
4
- export type { PerfPluginOptions, PerfThresholds, BundleMetrics } from './perf.types.js';
4
+ export type { BundleMetrics, PerfPluginOptions, PerfThresholds } from './perf.types.js';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,YAAY,EAAE,aAAa,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC"}
@@ -6,7 +6,7 @@ export declare class MemorySectionComponent {
6
6
  readonly deltaText: import("@angular/core").Signal<string>;
7
7
  readonly residualText: import("@angular/core").Signal<string>;
8
8
  readonly afterCreateColor: import("@angular/core").Signal<"" | "var(--prism-warn)">;
9
- readonly destroyColor: import("@angular/core").Signal<"" | "var(--prism-danger)" | "var(--prism-success)" | "var(--prism-text-ghost)">;
9
+ readonly destroyColor: import("@angular/core").Signal<"" | "var(--prism-text-ghost)" | "var(--prism-success)" | "var(--prism-danger)">;
10
10
  barPercent(bytes: number | undefined): number;
11
11
  formatMb(bytes: number | null | undefined): string;
12
12
  static ɵfac: i0.ɵɵFactoryDeclaration<MemorySectionComponent, never>;
@@ -1 +1 @@
1
- {"version":3,"file":"memory-section.component.d.ts","sourceRoot":"","sources":["../../src/memory/memory-section.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;;AAElE,qBA2Na,sBAAsB;IACjC,QAAQ,CAAC,aAAa,yDAAuC;IAE7D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAQtB;IAEH,QAAQ,CAAC,SAAS,yCAIf;IAEH,QAAQ,CAAC,YAAY,yCAIlB;IAEH,QAAQ,CAAC,gBAAgB,2DAItB;IAEH,QAAQ,CAAC,YAAY,kHAOlB;IAEH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM;IAK7C,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM;yCA7CvC,sBAAsB;2CAAtB,sBAAsB;CAiDlC"}
1
+ {"version":3,"file":"memory-section.component.d.ts","sourceRoot":"","sources":["../../src/memory/memory-section.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;;AAElE,qBAOa,sBAAsB;IAC/B,QAAQ,CAAC,aAAa,yDAAuC;IAE7D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAKtB;IAEH,QAAQ,CAAC,SAAS,yCAKf;IAEH,QAAQ,CAAC,YAAY,yCAKlB;IAEH,QAAQ,CAAC,gBAAgB,2DAKtB;IAEH,QAAQ,CAAC,YAAY,kHASlB;IAEH,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM;IAK7C,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM;yCA/CzC,sBAAsB;2CAAtB,sBAAsB;CAmDlC"}
@@ -146,11 +146,7 @@ export class MemorySectionComponent {
146
146
  ...(ngDevMode ? [{ debugName: "memoryService" }] : /* istanbul ignore next */ []));
147
147
  maxBytes = computed(() => {
148
148
  const svc = this.memoryService();
149
- const vals = [
150
- svc.beforeCreate()?.bytes ?? 0,
151
- svc.afterCreate()?.bytes ?? 0,
152
- svc.afterDestroy()?.bytes ?? 0,
153
- ];
149
+ const vals = [svc.beforeCreate()?.bytes ?? 0, svc.afterCreate()?.bytes ?? 0, svc.afterDestroy()?.bytes ?? 0];
154
150
  return Math.max(...vals);
155
151
  }, /* @ts-ignore */
156
152
  ...(ngDevMode ? [{ debugName: "maxBytes" }] : /* istanbul ignore next */ []));
@@ -203,80 +199,10 @@ export class MemorySectionComponent {
203
199
  } if (rf & 2) {
204
200
  let tmp_0_0;
205
201
  i0.ɵɵconditional((tmp_0_0 = ctx.memoryService()) ? 0 : -1, tmp_0_0);
206
- } }, styles: [".perf-body[_ngcontent-%COMP%] {\n padding: 20px 24px;\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n align-content: start;\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 .perf-card--block[_ngcontent-%COMP%] { display: block; }\n .perf-card-head[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n }\n .perf-card-name[_ngcontent-%COMP%] {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--%NS%prism-text-ghost);\n font-weight: 700;\n }\n .perf-card-status[_ngcontent-%COMP%] {\n font-size: 10px;\n padding: 2px 6px;\n border-radius: 3px;\n font-weight: 700;\n letter-spacing: 0.04em;\n }\n .perf-card-status.warn[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--%NS%prism-warn) 15%, transparent);\n color: var(--%NS%prism-warn);\n }\n .perf-card-val[_ngcontent-%COMP%] {\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 display: flex;\n align-items: baseline;\n gap: 4px;\n }\n .perf-card-val[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--%NS%prism-text-muted);\n font-weight: 500;\n }\n .perf-card-sub[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--%NS%prism-text-muted);\n margin-top: 4px;\n font-family: 'JetBrains Mono', monospace;\n }\n\n .perf-detail[_ngcontent-%COMP%] { padding: 0 24px 16px; }\n .perf-detail--last[_ngcontent-%COMP%] { padding-bottom: 20px; }\n\n .perf-bars[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 100px 1fr auto;\n gap: 10px;\n align-items: center;\n font-family: 'JetBrains Mono', monospace;\n font-size: 12px;\n margin-top: 8px;\n }\n .perf-bar-label[_ngcontent-%COMP%] { color: var(--%NS%prism-text-muted); }\n .perf-bar-track[_ngcontent-%COMP%] {\n height: 8px;\n background: var(--%NS%prism-input-bg);\n border-radius: 4px;\n overflow: hidden;\n }\n .perf-bar-fill[_ngcontent-%COMP%] {\n height: 100%;\n background: linear-gradient(90deg, var(--%NS%prism-primary-from), var(--%NS%prism-primary-to));\n border-radius: 4px;\n }\n .perf-bar-fill--warn[_ngcontent-%COMP%] {\n background: linear-gradient(90deg, var(--%NS%prism-warn), var(--%NS%prism-warn));\n opacity: 0.8;\n }\n .perf-bar-val[_ngcontent-%COMP%] { color: var(--%NS%prism-primary); }\n .perf-bar-val--warn[_ngcontent-%COMP%] { color: var(--%NS%prism-warn); }\n\n .perf-leak-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n border-radius: 5px;\n font-size: 12px;\n font-weight: 500;\n margin-top: 8px;\n }\n .perf-leak-badge--ok[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--%NS%prism-success) 10%, transparent);\n border: 1px solid color-mix(in srgb, var(--%NS%prism-success) 25%, transparent);\n color: var(--%NS%prism-success);\n }\n .perf-leak-badge--warn[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--%NS%prism-warn) 10%, transparent);\n border: 1px solid color-mix(in srgb, var(--%NS%prism-warn) 25%, transparent);\n color: var(--%NS%prism-warn);\n }\n .perf-leak-dot[_ngcontent-%COMP%] {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n }\n .perf-leak-badge--ok[_ngcontent-%COMP%] .perf-leak-dot[_ngcontent-%COMP%] { background: var(--%NS%prism-success); }\n .perf-leak-badge--warn[_ngcontent-%COMP%] .perf-leak-dot[_ngcontent-%COMP%] {\n background: var(--%NS%prism-warn);\n box-shadow: 0 0 6px var(--%NS%prism-warn);\n }\n\n .perf-disclaimer[_ngcontent-%COMP%] {\n margin-top: 8px;\n font-size: 12px;\n color: var(--%NS%prism-text-muted);\n font-family: 'JetBrains Mono', monospace;\n }\n\n .perf-empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n height: 100%;\n min-height: 180px;\n }\n .perf-empty-text[_ngcontent-%COMP%] { font-size: 12px; color: var(--%NS%prism-text-ghost); }\n .perf-empty-hint[_ngcontent-%COMP%] { font-size: 11px; color: var(--%NS%prism-text-ghost); opacity: 0.6; }"] });
202
+ } }, 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-status[_ngcontent-%COMP%] {\n padding: 2px 6px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.04em;\n border-radius: 3px;\n}\n\n.perf-card-status.warn[_ngcontent-%COMP%] {\n color: var(--%NS%prism-warn);\n background: color-mix(in srgb, var(--%NS%prism-warn) 15%, transparent);\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 16px;\n}\n\n.perf-detail--last[_ngcontent-%COMP%] {\n padding-bottom: 20px;\n}\n\n.perf-bars[_ngcontent-%COMP%] {\n display: grid;\n grid-template-columns: 100px 1fr auto;\n gap: 10px;\n align-items: center;\n margin-top: 8px;\n font-family: 'JetBrains Mono', monospace;\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: 8px;\n overflow: hidden;\n background: var(--%NS%prism-input-bg);\n border-radius: 4px;\n}\n\n.perf-bar-fill[_ngcontent-%COMP%] {\n height: 100%;\n background: linear-gradient(90deg, var(--%NS%prism-primary-from), var(--%NS%prism-primary-to));\n border-radius: 4px;\n}\n\n.perf-bar-fill--warn[_ngcontent-%COMP%] {\n background: linear-gradient(90deg, var(--%NS%prism-warn), var(--%NS%prism-warn));\n opacity: 0.8;\n}\n\n.perf-bar-val[_ngcontent-%COMP%] {\n color: var(--%NS%prism-primary);\n}\n\n.perf-bar-val--warn[_ngcontent-%COMP%] {\n color: var(--%NS%prism-warn);\n}\n\n.perf-leak-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n gap: 8px;\n align-items: center;\n padding: 8px 14px;\n margin-top: 8px;\n font-size: 12px;\n font-weight: 500;\n border-radius: 5px;\n}\n\n.perf-leak-badge--ok[_ngcontent-%COMP%] {\n color: var(--%NS%prism-success);\n background: color-mix(in srgb, var(--%NS%prism-success) 10%, transparent);\n border: 1px solid color-mix(in srgb, var(--%NS%prism-success) 25%, transparent);\n}\n\n.perf-leak-badge--warn[_ngcontent-%COMP%] {\n color: var(--%NS%prism-warn);\n background: color-mix(in srgb, var(--%NS%prism-warn) 10%, transparent);\n border: 1px solid color-mix(in srgb, var(--%NS%prism-warn) 25%, transparent);\n}\n\n.perf-leak-dot[_ngcontent-%COMP%] {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n}\n\n.perf-leak-badge--ok[_ngcontent-%COMP%] .perf-leak-dot[_ngcontent-%COMP%] {\n background: var(--%NS%prism-success);\n}\n\n.perf-leak-badge--warn[_ngcontent-%COMP%] .perf-leak-dot[_ngcontent-%COMP%] {\n background: var(--%NS%prism-warn);\n box-shadow: 0 0 6px var(--%NS%prism-warn);\n}\n\n.perf-disclaimer[_ngcontent-%COMP%] {\n margin-top: 8px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 12px;\n color: var(--%NS%prism-text-muted);\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}"] });
207
203
  }
208
204
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MemorySectionComponent, [{
209
205
  type: Component,
210
- args: [{ selector: 'perf-memory-section', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
211
- @if (memoryService(); as svc) {
212
- @if (!svc.available()) {
213
- <div class="perf-empty">
214
- <div class="perf-empty-text">Memory API not available</div>
215
- <div class="perf-empty-hint">performance.memory is Chrome-only (non-standard)</div>
216
- </div>
217
- } @else if (svc.snapshots().length > 0) {
218
- <div class="perf-body">
219
- <div class="perf-card">
220
- <div class="perf-card-head"><span class="perf-card-name">Before Create</span></div>
221
- <div class="perf-card-val">{{ formatMb(svc.beforeCreate()?.bytes) }}<span>MB</span></div>
222
- <div class="perf-card-sub">baseline heap</div>
223
- </div>
224
- <div class="perf-card">
225
- <div class="perf-card-head">
226
- <span class="perf-card-name">After Create</span>
227
- @if (svc.delta() !== null && svc.delta()! > 0) {
228
- <span class="perf-card-status warn">watch</span>
229
- }
230
- </div>
231
- <div class="perf-card-val" [style.color]="afterCreateColor()">{{ formatMb(svc.afterCreate()?.bytes) }}<span>MB</span></div>
232
- <div class="perf-card-sub">{{ deltaText() }}</div>
233
- </div>
234
- <div class="perf-card">
235
- <div class="perf-card-head"><span class="perf-card-name">After Destroy</span></div>
236
- <div class="perf-card-val" [style.color]="destroyColor()">{{ formatMb(svc.afterDestroy()?.bytes) }}<span>MB</span></div>
237
- </div>
238
- </div>
239
-
240
- @if (svc.beforeCreate() && svc.afterCreate()) {
241
- <div class="perf-detail">
242
- <div class="perf-card perf-card--block">
243
- <div class="perf-card-head"><span class="perf-card-name">Heap Comparison</span></div>
244
- <div class="perf-bars">
245
- <span class="perf-bar-label">before</span>
246
- <div class="perf-bar-track"><div class="perf-bar-fill" [style.width.%]="barPercent(svc.beforeCreate()?.bytes)"></div></div>
247
- <span class="perf-bar-val">{{ formatMb(svc.beforeCreate()?.bytes) }} MB</span>
248
- <span class="perf-bar-label">after create</span>
249
- <div class="perf-bar-track"><div class="perf-bar-fill perf-bar-fill--warn" [style.width.%]="barPercent(svc.afterCreate()?.bytes)"></div></div>
250
- <span class="perf-bar-val perf-bar-val--warn">{{ formatMb(svc.afterCreate()?.bytes) }} MB</span>
251
- </div>
252
- </div>
253
- </div>
254
- }
255
-
256
- <div class="perf-detail perf-detail--last">
257
- <div class="perf-card perf-card--block">
258
- <div class="perf-card-head"><span class="perf-card-name">Leak Analysis</span></div>
259
- @if (svc.leakStatus() === 'ok') {
260
- <div class="perf-leak-badge perf-leak-badge--ok">
261
- <span class="perf-leak-dot"></span>
262
- No significant leak detected
263
- </div>
264
- } @else if (svc.leakStatus() === 'warn') {
265
- <div class="perf-leak-badge perf-leak-badge--warn">
266
- <span class="perf-leak-dot"></span>
267
- Possible leak &mdash; +{{ formatMb(svc.residual()!) }} MB not reclaimed
268
- </div>
269
- }
270
- <div class="perf-disclaimer">&#9432; performance.memory is Chrome-only (non-standard). Not available in other browsers.</div>
271
- </div>
272
- </div>
273
- } @else {
274
- <div class="perf-empty">
275
- <div class="perf-empty-text">No memory snapshots yet</div>
276
- <div class="perf-empty-hint">Click Snapshot to capture heap data</div>
277
- </div>
278
- }
279
- }
280
- `, styles: ["\n .perf-body {\n padding: 20px 24px;\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n align-content: start;\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 .perf-card--block { display: block; }\n .perf-card-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n }\n .perf-card-name {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--prism-text-ghost);\n font-weight: 700;\n }\n .perf-card-status {\n font-size: 10px;\n padding: 2px 6px;\n border-radius: 3px;\n font-weight: 700;\n letter-spacing: 0.04em;\n }\n .perf-card-status.warn {\n background: color-mix(in srgb, var(--prism-warn) 15%, transparent);\n color: var(--prism-warn);\n }\n .perf-card-val {\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 display: flex;\n align-items: baseline;\n gap: 4px;\n }\n .perf-card-val span {\n font-size: 13px;\n color: var(--prism-text-muted);\n font-weight: 500;\n }\n .perf-card-sub {\n font-size: 11px;\n color: var(--prism-text-muted);\n margin-top: 4px;\n font-family: 'JetBrains Mono', monospace;\n }\n\n .perf-detail { padding: 0 24px 16px; }\n .perf-detail--last { padding-bottom: 20px; }\n\n .perf-bars {\n display: grid;\n grid-template-columns: 100px 1fr auto;\n gap: 10px;\n align-items: center;\n font-family: 'JetBrains Mono', monospace;\n font-size: 12px;\n margin-top: 8px;\n }\n .perf-bar-label { color: var(--prism-text-muted); }\n .perf-bar-track {\n height: 8px;\n background: var(--prism-input-bg);\n border-radius: 4px;\n overflow: hidden;\n }\n .perf-bar-fill {\n height: 100%;\n background: linear-gradient(90deg, var(--prism-primary-from), var(--prism-primary-to));\n border-radius: 4px;\n }\n .perf-bar-fill--warn {\n background: linear-gradient(90deg, var(--prism-warn), var(--prism-warn));\n opacity: 0.8;\n }\n .perf-bar-val { color: var(--prism-primary); }\n .perf-bar-val--warn { color: var(--prism-warn); }\n\n .perf-leak-badge {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n border-radius: 5px;\n font-size: 12px;\n font-weight: 500;\n margin-top: 8px;\n }\n .perf-leak-badge--ok {\n background: color-mix(in srgb, var(--prism-success) 10%, transparent);\n border: 1px solid color-mix(in srgb, var(--prism-success) 25%, transparent);\n color: var(--prism-success);\n }\n .perf-leak-badge--warn {\n background: color-mix(in srgb, var(--prism-warn) 10%, transparent);\n border: 1px solid color-mix(in srgb, var(--prism-warn) 25%, transparent);\n color: var(--prism-warn);\n }\n .perf-leak-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n }\n .perf-leak-badge--ok .perf-leak-dot { background: var(--prism-success); }\n .perf-leak-badge--warn .perf-leak-dot {\n background: var(--prism-warn);\n box-shadow: 0 0 6px var(--prism-warn);\n }\n\n .perf-disclaimer {\n margin-top: 8px;\n font-size: 12px;\n color: var(--prism-text-muted);\n font-family: 'JetBrains Mono', monospace;\n }\n\n .perf-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n height: 100%;\n min-height: 180px;\n }\n .perf-empty-text { font-size: 12px; color: var(--prism-text-ghost); }\n .perf-empty-hint { font-size: 11px; color: var(--prism-text-ghost); opacity: 0.6; }\n "] }]
206
+ args: [{ selector: 'perf-memory-section', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (memoryService(); as svc) {\n @if (!svc.available()) {\n <div class=\"perf-empty\">\n <div class=\"perf-empty-text\">Memory API not available</div>\n <div class=\"perf-empty-hint\">performance.memory is Chrome-only (non-standard)</div>\n </div>\n } @else if (svc.snapshots().length > 0) {\n <div class=\"perf-body\">\n <div class=\"perf-card\">\n <div class=\"perf-card-head\"><span class=\"perf-card-name\">Before Create</span></div>\n <div class=\"perf-card-val\">{{ formatMb(svc.beforeCreate()?.bytes) }}<span>MB</span></div>\n <div class=\"perf-card-sub\">baseline heap</div>\n </div>\n <div class=\"perf-card\">\n <div class=\"perf-card-head\">\n <span class=\"perf-card-name\">After Create</span>\n @if (svc.delta() !== null && svc.delta()! > 0) {\n <span class=\"perf-card-status warn\">watch</span>\n }\n </div>\n <div\n class=\"perf-card-val\"\n [style.color]=\"afterCreateColor()\"\n >{{ formatMb(svc.afterCreate()?.bytes) }}<span>MB</span></div\n >\n <div class=\"perf-card-sub\">{{ deltaText() }}</div>\n </div>\n <div class=\"perf-card\">\n <div class=\"perf-card-head\"><span class=\"perf-card-name\">After Destroy</span></div>\n <div\n class=\"perf-card-val\"\n [style.color]=\"destroyColor()\"\n >{{ formatMb(svc.afterDestroy()?.bytes) }}<span>MB</span></div\n >\n </div>\n </div>\n\n @if (svc.beforeCreate() && svc.afterCreate()) {\n <div class=\"perf-detail\">\n <div class=\"perf-card perf-card--block\">\n <div class=\"perf-card-head\"><span class=\"perf-card-name\">Heap Comparison</span></div>\n <div class=\"perf-bars\">\n <span class=\"perf-bar-label\">before</span>\n <div class=\"perf-bar-track\"\n ><div\n class=\"perf-bar-fill\"\n [style.width.%]=\"barPercent(svc.beforeCreate()?.bytes)\"></div\n ></div>\n <span class=\"perf-bar-val\">{{ formatMb(svc.beforeCreate()?.bytes) }} MB</span>\n <span class=\"perf-bar-label\">after create</span>\n <div class=\"perf-bar-track\"\n ><div\n class=\"perf-bar-fill perf-bar-fill--warn\"\n [style.width.%]=\"barPercent(svc.afterCreate()?.bytes)\"></div\n ></div>\n <span class=\"perf-bar-val perf-bar-val--warn\">{{ formatMb(svc.afterCreate()?.bytes) }} MB</span>\n </div>\n </div>\n </div>\n }\n\n <div class=\"perf-detail perf-detail--last\">\n <div class=\"perf-card perf-card--block\">\n <div class=\"perf-card-head\"><span class=\"perf-card-name\">Leak Analysis</span></div>\n @if (svc.leakStatus() === 'ok') {\n <div class=\"perf-leak-badge perf-leak-badge--ok\">\n <span class=\"perf-leak-dot\"></span>\n No significant leak detected\n </div>\n } @else if (svc.leakStatus() === 'warn') {\n <div class=\"perf-leak-badge perf-leak-badge--warn\">\n <span class=\"perf-leak-dot\"></span>\n Possible leak &mdash; +{{ formatMb(svc.residual()!) }} MB not reclaimed\n </div>\n }\n <div class=\"perf-disclaimer\">&#9432; performance.memory is Chrome-only (non-standard). Not available in other browsers.</div>\n </div>\n </div>\n } @else {\n <div class=\"perf-empty\">\n <div class=\"perf-empty-text\">No memory snapshots yet</div>\n <div class=\"perf-empty-hint\">Click Snapshot to capture heap data</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-status {\n padding: 2px 6px;\n font-size: 10px;\n font-weight: 700;\n letter-spacing: 0.04em;\n border-radius: 3px;\n}\n\n.perf-card-status.warn {\n color: var(--prism-warn);\n background: color-mix(in srgb, var(--prism-warn) 15%, transparent);\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 16px;\n}\n\n.perf-detail--last {\n padding-bottom: 20px;\n}\n\n.perf-bars {\n display: grid;\n grid-template-columns: 100px 1fr auto;\n gap: 10px;\n align-items: center;\n margin-top: 8px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 12px;\n}\n\n.perf-bar-label {\n color: var(--prism-text-muted);\n}\n\n.perf-bar-track {\n height: 8px;\n overflow: hidden;\n background: var(--prism-input-bg);\n border-radius: 4px;\n}\n\n.perf-bar-fill {\n height: 100%;\n background: linear-gradient(90deg, var(--prism-primary-from), var(--prism-primary-to));\n border-radius: 4px;\n}\n\n.perf-bar-fill--warn {\n background: linear-gradient(90deg, var(--prism-warn), var(--prism-warn));\n opacity: 0.8;\n}\n\n.perf-bar-val {\n color: var(--prism-primary);\n}\n\n.perf-bar-val--warn {\n color: var(--prism-warn);\n}\n\n.perf-leak-badge {\n display: inline-flex;\n gap: 8px;\n align-items: center;\n padding: 8px 14px;\n margin-top: 8px;\n font-size: 12px;\n font-weight: 500;\n border-radius: 5px;\n}\n\n.perf-leak-badge--ok {\n color: var(--prism-success);\n background: color-mix(in srgb, var(--prism-success) 10%, transparent);\n border: 1px solid color-mix(in srgb, var(--prism-success) 25%, transparent);\n}\n\n.perf-leak-badge--warn {\n color: var(--prism-warn);\n background: color-mix(in srgb, var(--prism-warn) 10%, transparent);\n border: 1px solid color-mix(in srgb, var(--prism-warn) 25%, transparent);\n}\n\n.perf-leak-dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n}\n\n.perf-leak-badge--ok .perf-leak-dot {\n background: var(--prism-success);\n}\n\n.perf-leak-badge--warn .perf-leak-dot {\n background: var(--prism-warn);\n box-shadow: 0 0 6px var(--prism-warn);\n}\n\n.perf-disclaimer {\n margin-top: 8px;\n font-family: 'JetBrains Mono', monospace;\n font-size: 12px;\n color: var(--prism-text-muted);\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"] }]
281
207
  }], null, { memoryService: [{ type: i0.Input, args: [{ isSignal: true, alias: "memoryService", required: true }] }] }); })();
282
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MemorySectionComponent, { className: "MemorySectionComponent", filePath: "memory/memory-section.component.ts", lineNumber: 223 }); })();
208
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MemorySectionComponent, { className: "MemorySectionComponent", filePath: "memory/memory-section.component.ts", lineNumber: 11 }); })();
@@ -14,7 +14,7 @@ export declare class PerfMemoryService implements PrismRendererHooks {
14
14
  readonly afterDestroy: import("@angular/core").Signal<HeapSnapshot | null>;
15
15
  readonly delta: import("@angular/core").Signal<number | null>;
16
16
  readonly residual: import("@angular/core").Signal<number | null>;
17
- readonly leakStatus: import("@angular/core").Signal<"ok" | "warn" | "unknown">;
17
+ readonly leakStatus: import("@angular/core").Signal<"warn" | "ok" | "unknown">;
18
18
  configure(gcDelayMs: number): void;
19
19
  onBeforeCreate(_selector: string): void;
20
20
  onAfterCreate(_selector: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"perf-memory.service.d.ts","sourceRoot":"","sources":["../../src/memory/perf-memory.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,qBAAa,iBAAkB,YAAW,kBAAkB;IAC1D,MAAM,CAAC,WAAW,IAAI,iBAAiB;IAKvC,QAAQ,CAAC,SAAS,yDAA8B;IAChD,QAAQ,CAAC,SAAS,0CAAiF;IAEnG,OAAO,CAAC,OAAO,CAAO;IAEtB,QAAQ,CAAC,YAAY,sDAAsD;IAC3E,QAAQ,CAAC,WAAW,sDAAqD;IACzE,QAAQ,CAAC,YAAY,sDAAsD;IAE3E,QAAQ,CAAC,KAAK,gDAKX;IAEH,QAAQ,CAAC,QAAQ,gDAKd;IAEH,QAAQ,CAAC,UAAU,4DAIhB;IAEH,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIlC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIvC,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAItC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIvC,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IASjC,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,YAAY;CAGrB"}
1
+ {"version":3,"file":"perf-memory.service.d.ts","sourceRoot":"","sources":["../../src/memory/perf-memory.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEhE,MAAM,WAAW,YAAY;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACrB;AAID,qBAAa,iBAAkB,YAAW,kBAAkB;IACxD,MAAM,CAAC,WAAW,IAAI,iBAAiB;IAKvC,QAAQ,CAAC,SAAS,yDAA8B;IAChD,QAAQ,CAAC,SAAS,0CAAiF;IAEnG,OAAO,CAAC,OAAO,CAAO;IAEtB,QAAQ,CAAC,YAAY,sDAAsD;IAC3E,QAAQ,CAAC,WAAW,sDAAqD;IACzE,QAAQ,CAAC,YAAY,sDAAsD;IAE3E,QAAQ,CAAC,KAAK,gDAMX;IAEH,QAAQ,CAAC,QAAQ,gDAMd;IAEH,QAAQ,CAAC,UAAU,4DAKhB;IAEH,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIlC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIvC,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAItC,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAIvC,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAOjC,KAAK,IAAI,IAAI;IAIb,OAAO,CAAC,YAAY;CAGvB"}
@@ -1,4 +1,4 @@
1
- import { signal, computed } from '@angular/core';
1
+ import { computed, signal } from '@angular/core';
2
2
  let instance = null;
3
3
  export class PerfMemoryService {
4
4
  static getInstance() {
@@ -56,10 +56,7 @@ export class PerfMemoryService {
56
56
  if (!this.available())
57
57
  return;
58
58
  const mem = performance.memory;
59
- this.snapshots.update((arr) => [
60
- ...arr.filter((s) => s.label !== label),
61
- { label, bytes: mem.usedJSHeapSize, timestamp: Date.now() },
62
- ]);
59
+ this.snapshots.update((arr) => [...arr.filter((s) => s.label !== label), { label, bytes: mem.usedJSHeapSize, timestamp: Date.now() }]);
63
60
  }
64
61
  clear() {
65
62
  this.snapshots.set([]);
@@ -1,6 +1,6 @@
1
+ import { PerfMemoryService } from './memory/perf-memory.service.js';
1
2
  import type { BundleMetrics, PerfThresholds } from './perf.types.js';
2
3
  import { PerfRenderService } from './render/perf-render.service.js';
3
- import { PerfMemoryService } from './memory/perf-memory.service.js';
4
4
  import * as i0 from "@angular/core";
5
5
  type SubTab = 'bundle' | 'render' | 'memory';
6
6
  export declare class PerfPanelComponent {
@@ -1 +1 @@
1
- {"version":3,"file":"perf-panel.component.d.ts","sourceRoot":"","sources":["../src/perf-panel.component.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAErE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;;AAKpE,KAAK,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE7C,qBAoNa,kBAAkB;IAC7B,QAAQ,CAAC,eAAe,+CAAwB;IAEhD,QAAQ,CAAC,SAAS,iDAA4B;IAC9C,QAAQ,CAAC,aAAa,oBAAmC;IACzD,QAAQ,CAAC,aAAa,oBAAmC;IAEzD,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAsB;IAEzD,sBAAsB,IAAI,IAAI;IAK9B,QAAQ,CAAC,aAAa,uDAOnB;yCArBQ,kBAAkB;2CAAlB,kBAAkB;CAsB9B"}
1
+ {"version":3,"file":"perf-panel.component.d.ts","sourceRoot":"","sources":["../src/perf-panel.component.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAErE,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;;AAGpE,KAAK,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE7C,qBAQa,kBAAkB;IAC3B,QAAQ,CAAC,eAAe,+CAAwB;IAEhD,QAAQ,CAAC,SAAS,iDAA4B;IAC9C,QAAQ,CAAC,aAAa,oBAAmC;IACzD,QAAQ,CAAC,aAAa,oBAAmC;IAEzD,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAsB;IAEzD,sBAAsB,IAAI,IAAI;IAK9B,QAAQ,CAAC,aAAa,uDAQnB;yCAtBM,kBAAkB;2CAAlB,kBAAkB;CAuB9B"}
@@ -1,10 +1,10 @@
1
- import { ChangeDetectionStrategy, Component, computed, input, signal, } from '@angular/core';
1
+ import { ChangeDetectionStrategy, Component, computed, input, signal } from '@angular/core';
2
+ import { BundleSectionComponent } from './bundle/bundle-section.component.js';
3
+ import { MemorySectionComponent } from './memory/memory-section.component.js';
4
+ import { PerfMemoryService } from './memory/perf-memory.service.js';
2
5
  import { DEFAULT_THRESHOLDS } from './perf.types.js';
3
6
  import { PerfRenderService } from './render/perf-render.service.js';
4
- import { PerfMemoryService } from './memory/perf-memory.service.js';
5
- import { BundleSectionComponent } from './bundle/bundle-section.component.js';
6
7
  import { RenderSectionComponent } from './render/render-section.component.js';
7
- import { MemorySectionComponent } from './memory/memory-section.component.js';
8
8
  import * as i0 from "@angular/core";
9
9
  function PerfPanelComponent_Conditional_9_Conditional_0_Template(rf, ctx) { if (rf & 1) {
10
10
  const _r2 = i0.ɵɵgetCurrentView();
@@ -137,95 +137,10 @@ export class PerfPanelComponent {
137
137
  i0.ɵɵconditional(ctx.renderService.cdRunCount() > 0 ? 11 : -1);
138
138
  i0.ɵɵadvance(2);
139
139
  i0.ɵɵconditional((tmp_6_0 = ctx.activeTab()) === "bundle" ? 13 : tmp_6_0 === "render" ? 14 : tmp_6_0 === "memory" ? 15 : -1);
140
- } }, dependencies: [BundleSectionComponent,
141
- RenderSectionComponent,
142
- MemorySectionComponent], styles: ["[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n }\n\n .prism-perf-panel[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n }\n\n .prism-subtabs[_ngcontent-%COMP%] {\n display: flex;\n gap: 2px;\n padding: 10px 20px 0;\n border-bottom: 1px solid var(--%NS%prism-border);\n background: var(--%NS%prism-bg-elevated);\n position: sticky;\n top: 0;\n z-index: 2;\n }\n\n .prism-st-tab[_ngcontent-%COMP%] {\n padding: 7px 12px;\n font-size: 12px;\n font-weight: 500;\n color: var(--%NS%prism-text-muted);\n border-radius: 6px 6px 0 0;\n position: relative;\n display: flex;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n background: transparent;\n border: none;\n font-family: var(--%NS%font-sans);\n transition: color var(--%NS%dur-fast);\n }\n .prism-st-tab[_ngcontent-%COMP%]:hover { color: var(--%NS%prism-text-2); }\n .prism-st-tab--active[_ngcontent-%COMP%] { color: var(--%NS%prism-text); }\n .prism-st-tab--%NS%active[_ngcontent-%COMP%]::after {\n content: '';\n position: absolute;\n left: 8px;\n right: 8px;\n bottom: -1px;\n height: 2px;\n background: var(--%NS%prism-primary);\n border-radius: 1px;\n }\n\n .prism-subtabs-actions[_ngcontent-%COMP%] {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .prism-perf-cd-badge[_ngcontent-%COMP%] {\n font-family: var(--%NS%font-mono, 'JetBrains Mono', monospace);\n font-size: 11px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 0 8px;\n height: 24px;\n background: color-mix(in srgb, var(--%NS%prism-primary) 12%, transparent);\n color: var(--%NS%prism-primary);\n border: 1px solid color-mix(in srgb, var(--%NS%prism-primary) 25%, transparent);\n border-radius: 5px;\n }\n .prism-tool-btn[_ngcontent-%COMP%] {\n height: 24px;\n padding: 0 9px;\n border-radius: 5px;\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 11.5px;\n color: var(--%NS%prism-text-muted);\n transition: all 0.12s;\n font-weight: 500;\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--%NS%font-sans);\n }\n .prism-tool-btn[_ngcontent-%COMP%]:hover {\n color: var(--%NS%prism-text);\n background: color-mix(in srgb, var(--%NS%prism-primary) 8%, transparent);\n }\n\n .prism-perf-status[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--%NS%prism-text-muted);\n font-family: var(--%NS%font-mono);\n display: flex;\n align-items: center;\n gap: 5px;\n }\n .prism-perf-status-dot[_ngcontent-%COMP%] {\n display: inline-block;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: var(--%NS%prism-success);\n box-shadow: 0 0 6px var(--%NS%prism-success);\n animation: _ngcontent-%COMP%_prism-perf-pulse 2s ease-in-out infinite;\n }\n @keyframes _ngcontent-%COMP%_prism-perf-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n }\n\n .prism-perf-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n min-height: 0;\n }"] });
140
+ } }, dependencies: [BundleSectionComponent, RenderSectionComponent, MemorySectionComponent], styles: ["[_nghost-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n\n.prism-perf-panel[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n\n.prism-subtabs[_ngcontent-%COMP%] {\n position: sticky;\n top: 0;\n z-index: 2;\n display: flex;\n gap: 2px;\n padding: 10px 20px 0;\n background: var(--%NS%prism-bg-elevated);\n border-bottom: 1px solid var(--%NS%prism-border);\n}\n\n.prism-st-tab[_ngcontent-%COMP%] {\n position: relative;\n display: flex;\n gap: 6px;\n align-items: center;\n padding: 7px 12px;\n font-family: var(--%NS%font-sans);\n font-size: 12px;\n font-weight: 500;\n color: var(--%NS%prism-text-muted);\n cursor: pointer;\n background: transparent;\n border: none;\n border-radius: 6px 6px 0 0;\n transition: color var(--%NS%dur-fast);\n}\n\n.prism-st-tab[_ngcontent-%COMP%]:hover {\n color: var(--%NS%prism-text-2);\n}\n\n.prism-st-tab--active[_ngcontent-%COMP%] {\n color: var(--%NS%prism-text);\n}\n\n.prism-st-tab--%NS%active[_ngcontent-%COMP%]::after {\n position: absolute;\n right: 8px;\n bottom: -1px;\n left: 8px;\n height: 2px;\n content: '';\n background: var(--%NS%prism-primary);\n border-radius: 1px;\n}\n\n.prism-subtabs-actions[_ngcontent-%COMP%] {\n display: flex;\n gap: 8px;\n align-items: center;\n margin-left: auto;\n}\n\n.prism-perf-cd-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 24px;\n padding: 0 8px;\n font-family: var(--%NS%font-mono, 'JetBrains Mono', monospace);\n font-size: 11px;\n color: var(--%NS%prism-primary);\n background: color-mix(in srgb, var(--%NS%prism-primary) 12%, transparent);\n border: 1px solid color-mix(in srgb, var(--%NS%prism-primary) 25%, transparent);\n border-radius: 5px;\n}\n\n.prism-tool-btn[_ngcontent-%COMP%] {\n display: flex;\n gap: 5px;\n align-items: center;\n height: 24px;\n padding: 0 9px;\n font-family: var(--%NS%font-sans);\n font-size: 11.5px;\n font-weight: 500;\n color: var(--%NS%prism-text-muted);\n cursor: pointer;\n background: none;\n border: none;\n border-radius: 5px;\n transition: all 0.12s;\n}\n\n.prism-tool-btn[_ngcontent-%COMP%]:hover {\n color: var(--%NS%prism-text);\n background: color-mix(in srgb, var(--%NS%prism-primary) 8%, transparent);\n}\n\n.prism-perf-status[_ngcontent-%COMP%] {\n display: flex;\n gap: 5px;\n align-items: center;\n font-family: var(--%NS%font-mono);\n font-size: 11px;\n color: var(--%NS%prism-text-muted);\n}\n\n.prism-perf-status-dot[_ngcontent-%COMP%] {\n display: inline-block;\n width: 5px;\n height: 5px;\n background: var(--%NS%prism-success);\n border-radius: 50%;\n box-shadow: 0 0 6px var(--%NS%prism-success);\n animation: _ngcontent-%COMP%_prism-perf-pulse 2s ease-in-out infinite;\n}\n\n@keyframes _ngcontent-%COMP%_prism-perf-pulse {\n 0%,\n 100% {\n opacity: 1;\n }\n\n 50% {\n opacity: 0.4;\n }\n}\n\n.prism-perf-content[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n}"] });
143
141
  }
144
142
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PerfPanelComponent, [{
145
143
  type: Component,
146
- args: [{ selector: 'prism-perf-panel', standalone: true, imports: [
147
- BundleSectionComponent,
148
- RenderSectionComponent,
149
- MemorySectionComponent,
150
- ], changeDetection: ChangeDetectionStrategy.OnPush, template: `
151
- <div class="prism-perf-panel">
152
- <div class="prism-subtabs">
153
- <button
154
- class="prism-st-tab"
155
- [class.prism-st-tab--active]="activeTab() === 'bundle'"
156
- (click)="activeTab.set('bundle')"
157
- >
158
- Bundle
159
- </button>
160
- <button
161
- class="prism-st-tab"
162
- [class.prism-st-tab--active]="activeTab() === 'render'"
163
- (click)="activeTab.set('render')"
164
- >
165
- Render
166
- </button>
167
- <button
168
- class="prism-st-tab"
169
- [class.prism-st-tab--active]="activeTab() === 'memory'"
170
- (click)="activeTab.set('memory')"
171
- >
172
- Memory
173
- </button>
174
- <div class="prism-subtabs-actions">
175
- @if (activeTab() === 'render') {
176
- @if (renderService.isRecording()) {
177
- <button class="prism-tool-btn" (click)="renderService.stopRecording()">
178
- &#9208; Pause
179
- </button>
180
- } @else {
181
- <button class="prism-tool-btn" (click)="renderService.startRecording()">
182
- &#9654; Profile
183
- </button>
184
- }
185
- <button class="prism-tool-btn" (click)="renderService.clear()">
186
- &#10005; Clear
187
- </button>
188
- @if (renderService.isRecording()) {
189
- <span class="prism-perf-status">
190
- <span class="prism-perf-status-dot"></span> live &middot;
191
- {{ renderService.sampleCount() }} samples
192
- </span>
193
- }
194
- }
195
- @if (activeTab() === 'memory') {
196
- <button class="prism-tool-btn" (click)="captureMemorySnapshots()">
197
- &#9654; Snapshot
198
- </button>
199
- <button class="prism-tool-btn" (click)="memoryService.clear()">
200
- &#10005; Clear
201
- </button>
202
- }
203
- @if (renderService.cdRunCount() > 0) {
204
- <span class="prism-perf-cd-badge">
205
- {{ renderService.cdRunCount() }} CD runs
206
- </span>
207
- }
208
- </div>
209
- </div>
210
-
211
- <div class="prism-perf-content">
212
- @switch (activeTab()) { @case ('bundle') {
213
- <perf-bundle-section
214
- [metrics]="bundleMetrics()"
215
- [warnKb]="thresholds.bundleWarnKb"
216
- [critKb]="thresholds.bundleCritKb"
217
- />
218
- } @case ('render') {
219
- <perf-render-section
220
- [renderService]="renderService"
221
- [warnMs]="thresholds.renderWarnMs"
222
- [critMs]="thresholds.renderCritMs"
223
- />
224
- } @case ('memory') {
225
- <perf-memory-section [memoryService]="memoryService" />
226
- } }
227
- </div>
228
- </div>
229
- `, styles: ["\n :host {\n display: flex;\n flex-direction: column;\n height: 100%;\n }\n\n .prism-perf-panel {\n display: flex;\n flex-direction: column;\n height: 100%;\n }\n\n .prism-subtabs {\n display: flex;\n gap: 2px;\n padding: 10px 20px 0;\n border-bottom: 1px solid var(--prism-border);\n background: var(--prism-bg-elevated);\n position: sticky;\n top: 0;\n z-index: 2;\n }\n\n .prism-st-tab {\n padding: 7px 12px;\n font-size: 12px;\n font-weight: 500;\n color: var(--prism-text-muted);\n border-radius: 6px 6px 0 0;\n position: relative;\n display: flex;\n align-items: center;\n gap: 6px;\n cursor: pointer;\n background: transparent;\n border: none;\n font-family: var(--font-sans);\n transition: color var(--dur-fast);\n }\n .prism-st-tab:hover { color: var(--prism-text-2); }\n .prism-st-tab--active { color: var(--prism-text); }\n .prism-st-tab--active::after {\n content: '';\n position: absolute;\n left: 8px;\n right: 8px;\n bottom: -1px;\n height: 2px;\n background: var(--prism-primary);\n border-radius: 1px;\n }\n\n .prism-subtabs-actions {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .prism-perf-cd-badge {\n font-family: var(--font-mono, 'JetBrains Mono', monospace);\n font-size: 11px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n padding: 0 8px;\n height: 24px;\n background: color-mix(in srgb, var(--prism-primary) 12%, transparent);\n color: var(--prism-primary);\n border: 1px solid color-mix(in srgb, var(--prism-primary) 25%, transparent);\n border-radius: 5px;\n }\n .prism-tool-btn {\n height: 24px;\n padding: 0 9px;\n border-radius: 5px;\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 11.5px;\n color: var(--prism-text-muted);\n transition: all 0.12s;\n font-weight: 500;\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n }\n .prism-tool-btn:hover {\n color: var(--prism-text);\n background: color-mix(in srgb, var(--prism-primary) 8%, transparent);\n }\n\n .prism-perf-status {\n font-size: 11px;\n color: var(--prism-text-muted);\n font-family: var(--font-mono);\n display: flex;\n align-items: center;\n gap: 5px;\n }\n .prism-perf-status-dot {\n display: inline-block;\n width: 5px;\n height: 5px;\n border-radius: 50%;\n background: var(--prism-success);\n box-shadow: 0 0 6px var(--prism-success);\n animation: prism-perf-pulse 2s ease-in-out infinite;\n }\n @keyframes prism-perf-pulse {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.4; }\n }\n\n .prism-perf-content {\n flex: 1;\n overflow-y: auto;\n min-height: 0;\n }\n "] }]
144
+ args: [{ selector: 'prism-perf-panel', standalone: true, imports: [BundleSectionComponent, RenderSectionComponent, MemorySectionComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"prism-perf-panel\">\n <div class=\"prism-subtabs\">\n <button\n class=\"prism-st-tab\"\n [class.prism-st-tab--active]=\"activeTab() === 'bundle'\"\n (click)=\"activeTab.set('bundle')\">\n Bundle\n </button>\n <button\n class=\"prism-st-tab\"\n [class.prism-st-tab--active]=\"activeTab() === 'render'\"\n (click)=\"activeTab.set('render')\">\n Render\n </button>\n <button\n class=\"prism-st-tab\"\n [class.prism-st-tab--active]=\"activeTab() === 'memory'\"\n (click)=\"activeTab.set('memory')\">\n Memory\n </button>\n <div class=\"prism-subtabs-actions\">\n @if (activeTab() === 'render') {\n @if (renderService.isRecording()) {\n <button\n class=\"prism-tool-btn\"\n (click)=\"renderService.stopRecording()\">\n &#9208; Pause\n </button>\n } @else {\n <button\n class=\"prism-tool-btn\"\n (click)=\"renderService.startRecording()\">\n &#9654; Profile\n </button>\n }\n <button\n class=\"prism-tool-btn\"\n (click)=\"renderService.clear()\">\n &#10005; Clear\n </button>\n @if (renderService.isRecording()) {\n <span class=\"prism-perf-status\"> <span class=\"prism-perf-status-dot\"></span> live &middot; {{ renderService.sampleCount() }} samples </span>\n }\n }\n @if (activeTab() === 'memory') {\n <button\n class=\"prism-tool-btn\"\n (click)=\"captureMemorySnapshots()\">\n &#9654; Snapshot\n </button>\n <button\n class=\"prism-tool-btn\"\n (click)=\"memoryService.clear()\">\n &#10005; Clear\n </button>\n }\n @if (renderService.cdRunCount() > 0) {\n <span class=\"prism-perf-cd-badge\"> {{ renderService.cdRunCount() }} CD runs </span>\n }\n </div>\n </div>\n\n <div class=\"prism-perf-content\">\n @switch (activeTab()) {\n @case ('bundle') {\n <perf-bundle-section\n [metrics]=\"bundleMetrics()\"\n [warnKb]=\"thresholds.bundleWarnKb\"\n [critKb]=\"thresholds.bundleCritKb\" />\n }\n @case ('render') {\n <perf-render-section\n [renderService]=\"renderService\"\n [warnMs]=\"thresholds.renderWarnMs\"\n [critMs]=\"thresholds.renderCritMs\" />\n }\n @case ('memory') {\n <perf-memory-section [memoryService]=\"memoryService\" />\n }\n }\n </div>\n</div>\n", styles: [":host {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n\n.prism-perf-panel {\n display: flex;\n flex-direction: column;\n height: 100%;\n}\n\n.prism-subtabs {\n position: sticky;\n top: 0;\n z-index: 2;\n display: flex;\n gap: 2px;\n padding: 10px 20px 0;\n background: var(--prism-bg-elevated);\n border-bottom: 1px solid var(--prism-border);\n}\n\n.prism-st-tab {\n position: relative;\n display: flex;\n gap: 6px;\n align-items: center;\n padding: 7px 12px;\n font-family: var(--font-sans);\n font-size: 12px;\n font-weight: 500;\n color: var(--prism-text-muted);\n cursor: pointer;\n background: transparent;\n border: none;\n border-radius: 6px 6px 0 0;\n transition: color var(--dur-fast);\n}\n\n.prism-st-tab:hover {\n color: var(--prism-text-2);\n}\n\n.prism-st-tab--active {\n color: var(--prism-text);\n}\n\n.prism-st-tab--active::after {\n position: absolute;\n right: 8px;\n bottom: -1px;\n left: 8px;\n height: 2px;\n content: '';\n background: var(--prism-primary);\n border-radius: 1px;\n}\n\n.prism-subtabs-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n margin-left: auto;\n}\n\n.prism-perf-cd-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n height: 24px;\n padding: 0 8px;\n font-family: var(--font-mono, 'JetBrains Mono', monospace);\n font-size: 11px;\n color: var(--prism-primary);\n background: color-mix(in srgb, var(--prism-primary) 12%, transparent);\n border: 1px solid color-mix(in srgb, var(--prism-primary) 25%, transparent);\n border-radius: 5px;\n}\n\n.prism-tool-btn {\n display: flex;\n gap: 5px;\n align-items: center;\n height: 24px;\n padding: 0 9px;\n font-family: var(--font-sans);\n font-size: 11.5px;\n font-weight: 500;\n color: var(--prism-text-muted);\n cursor: pointer;\n background: none;\n border: none;\n border-radius: 5px;\n transition: all 0.12s;\n}\n\n.prism-tool-btn:hover {\n color: var(--prism-text);\n background: color-mix(in srgb, var(--prism-primary) 8%, transparent);\n}\n\n.prism-perf-status {\n display: flex;\n gap: 5px;\n align-items: center;\n font-family: var(--font-mono);\n font-size: 11px;\n color: var(--prism-text-muted);\n}\n\n.prism-perf-status-dot {\n display: inline-block;\n width: 5px;\n height: 5px;\n background: var(--prism-success);\n border-radius: 50%;\n box-shadow: 0 0 6px var(--prism-success);\n animation: prism-perf-pulse 2s ease-in-out infinite;\n}\n\n@keyframes prism-perf-pulse {\n 0%,\n 100% {\n opacity: 1;\n }\n\n 50% {\n opacity: 0.4;\n }\n}\n\n.prism-perf-content {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n}\n"] }]
230
145
  }], null, { activeComponent: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeComponent", required: false }] }] }); })();
231
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PerfPanelComponent, { className: "PerfPanelComponent", filePath: "perf-panel.component.ts", lineNumber: 230 }); })();
146
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PerfPanelComponent, { className: "PerfPanelComponent", filePath: "perf-panel.component.ts", lineNumber: 20 }); })();
@@ -1 +1 @@
1
- {"version":3,"file":"perf-plugin.browser.d.ts","sourceRoot":"","sources":["../src/perf-plugin.browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,wBAAgB,UAAU,CAAC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,aAAa,CAetE"}
1
+ {"version":3,"file":"perf-plugin.browser.d.ts","sourceRoot":"","sources":["../src/perf-plugin.browser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,wBAAgB,UAAU,CAAC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,aAAa,CActE"}
@@ -8,8 +8,8 @@ export function perfPlugin(_options) {
8
8
  icon: 'activity',
9
9
  loadComponent: () => import('./perf-panel.component.js').then((m) => m.PerfPanelComponent),
10
10
  position: 'bottom',
11
- placement: 'addon',
12
- },
13
- ],
11
+ placement: 'addon'
12
+ }
13
+ ]
14
14
  };
15
15
  }
@@ -1 +1 @@
1
- {"version":3,"file":"perf-plugin.d.ts","sourceRoot":"","sources":["../src/perf-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,wBAAgB,UAAU,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,aAAa,CA+BrE"}
1
+ {"version":3,"file":"perf-plugin.d.ts","sourceRoot":"","sources":["../src/perf-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,wBAAgB,UAAU,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,aAAa,CA4BrE"}
@@ -10,9 +10,9 @@ export function perfPlugin(options) {
10
10
  ...component.showcaseConfig,
11
11
  meta: {
12
12
  ...component.showcaseConfig.meta,
13
- perf: { bundle },
14
- },
15
- },
13
+ perf: { bundle }
14
+ }
15
+ }
16
16
  };
17
17
  },
18
18
  panels: [
@@ -20,8 +20,8 @@ export function perfPlugin(options) {
20
20
  id: 'perf',
21
21
  label: 'Performance',
22
22
  loadComponent: () => import('./perf-panel.component.js').then((m) => m.PerfPanelComponent),
23
- position: 'bottom',
24
- },
25
- ],
23
+ position: 'bottom'
24
+ }
25
+ ]
26
26
  };
27
27
  }
@@ -1 +1 @@
1
- {"version":3,"file":"perf.types.d.ts","sourceRoot":"","sources":["../src/perf.types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,UAAU,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE;QACP,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;KAC3B,CAAC;IACF,MAAM,CAAC,EAAE;QACP,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,CAAC;IACF,MAAM,CAAC,EAAE;QACP,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;CACH;AAED,eAAO,MAAM,kBAAkB,EAAE,cAOhC,CAAC"}
1
+ {"version":3,"file":"perf.types.d.ts","sourceRoot":"","sources":["../src/perf.types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,cAAc;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAC9B,UAAU,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IACrC,MAAM,CAAC,EAAE;QACL,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;KAC7B,CAAC;IACF,MAAM,CAAC,EAAE;QACL,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,OAAO,CAAC;KACvB,CAAC;IACF,MAAM,CAAC,EAAE;QACL,SAAS,CAAC,EAAE,MAAM,CAAC;KACtB,CAAC;CACL;AAED,eAAO,MAAM,kBAAkB,EAAE,cAOhC,CAAC"}
@@ -4,5 +4,5 @@ export const DEFAULT_THRESHOLDS = {
4
4
  renderWarnMs: 5,
5
5
  renderCritMs: 16,
6
6
  memoryWarnMb: 5,
7
- memoryLeakMb: 0.5,
7
+ memoryLeakMb: 0.5
8
8
  };
@@ -1 +1 @@
1
- {"version":3,"file":"perf-render.service.d.ts","sourceRoot":"","sources":["../../src/render/perf-render.service.ts"],"names":[],"mappings":"AAIA,qBAAa,iBAAiB;IAC5B,MAAM,CAAC,WAAW,IAAI,iBAAiB;IAKvC,QAAQ,CAAC,WAAW,kDAAiB;IACrC,QAAQ,CAAC,aAAa,wDAA+B;IACrD,QAAQ,CAAC,SAAS,mDAAwB;IAC1C,QAAQ,CAAC,UAAU,iDAAa;IAEhC,OAAO,CAAC,QAAQ,CAAoC;IACpD,OAAO,CAAC,UAAU,CAAM;IAExB,QAAQ,CAAC,WAAW,yCAIjB;IAEH,QAAQ,CAAC,WAAW,yCAMjB;IAEH,QAAQ,CAAC,WAAW,yCAIjB;IAEH,QAAQ,CAAC,YAAY,gDAGlB;IAEH,QAAQ,CAAC,WAAW,yCAA2C;IAE/D,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAInC,cAAc,IAAI,IAAI;IAqBtB,aAAa,IAAI,IAAI;IAMrB,KAAK,IAAI,IAAI;CAKd"}
1
+ {"version":3,"file":"perf-render.service.d.ts","sourceRoot":"","sources":["../../src/render/perf-render.service.ts"],"names":[],"mappings":"AAIA,qBAAa,iBAAiB;IAC1B,MAAM,CAAC,WAAW,IAAI,iBAAiB;IAKvC,QAAQ,CAAC,WAAW,kDAAiB;IACrC,QAAQ,CAAC,aAAa,wDAA+B;IACrD,QAAQ,CAAC,SAAS,mDAAwB;IAC1C,QAAQ,CAAC,UAAU,iDAAa;IAEhC,OAAO,CAAC,QAAQ,CAAoC;IACpD,OAAO,CAAC,UAAU,CAAM;IAExB,QAAQ,CAAC,WAAW,yCAKjB;IAEH,QAAQ,CAAC,WAAW,yCAQjB;IAEH,QAAQ,CAAC,WAAW,yCAKjB;IAEH,QAAQ,CAAC,YAAY,gDAIlB;IAEH,QAAQ,CAAC,WAAW,yCAA2C;IAE/D,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAInC,cAAc,IAAI,IAAI;IAsBtB,aAAa,IAAI,IAAI;IAMrB,KAAK,IAAI,IAAI;CAKhB"}
@@ -1,4 +1,4 @@
1
- import { signal, computed } from '@angular/core';
1
+ import { computed, signal } from '@angular/core';
2
2
  let instance = null;
3
3
  export class PerfRenderService {
4
4
  static getInstance() {
@@ -4,7 +4,7 @@ export declare class RenderSectionComponent {
4
4
  readonly renderService: import("@angular/core").InputSignal<PerfRenderService>;
5
5
  readonly warnMs: import("@angular/core").InputSignal<number>;
6
6
  readonly critMs: import("@angular/core").InputSignal<number>;
7
- readonly initialClass: import("@angular/core").Signal<"warn" | "" | "crit">;
7
+ readonly initialClass: import("@angular/core").Signal<"" | "crit" | "warn">;
8
8
  formatMs(value: number | null): string;
9
9
  colorFor(value: number | null): string;
10
10
  static ɵfac: i0.ɵɵFactoryDeclaration<RenderSectionComponent, never>;
@@ -1 +1 @@
1
- {"version":3,"file":"render-section.component.d.ts","sourceRoot":"","sources":["../../src/render/render-section.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;;AAGlE,qBA+Ha,sBAAsB;IACjC,QAAQ,CAAC,aAAa,yDAAuC;IAC7D,QAAQ,CAAC,MAAM,8CAAY;IAC3B,QAAQ,CAAC,MAAM,8CAAa;IAE5B,QAAQ,CAAC,YAAY,uDAMlB;IAEH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;IAKtC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;yCAlB3B,sBAAsB;2CAAtB,sBAAsB;CAwBlC"}
1
+ {"version":3,"file":"render-section.component.d.ts","sourceRoot":"","sources":["../../src/render/render-section.component.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;;AAGlE,qBAQa,sBAAsB;IAC/B,QAAQ,CAAC,aAAa,yDAAuC;IAC7D,QAAQ,CAAC,MAAM,8CAAY;IAC3B,QAAQ,CAAC,MAAM,8CAAa;IAE5B,QAAQ,CAAC,YAAY,uDAOlB;IAEH,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;IAKtC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM;yCAnB7B,sBAAsB;2CAAtB,sBAAsB;CAyBlC"}
@@ -146,57 +146,10 @@ export class RenderSectionComponent {
146
146
  } if (rf & 2) {
147
147
  let tmp_0_0;
148
148
  i0.ɵɵconditional((tmp_0_0 = ctx.renderService()) ? 0 : -1, tmp_0_0);
149
- } }, dependencies: [SparklineComponent], styles: [".perf-body[_ngcontent-%COMP%] {\n padding: 20px 24px;\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n align-content: start;\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 .perf-card--block[_ngcontent-%COMP%] { display: block; }\n .perf-card-head[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n }\n .perf-card-name[_ngcontent-%COMP%] {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--%NS%prism-text-ghost);\n font-weight: 700;\n }\n .perf-card-val[_ngcontent-%COMP%] {\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 display: flex;\n align-items: baseline;\n gap: 4px;\n }\n .perf-card-val[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--%NS%prism-text-muted);\n font-weight: 500;\n }\n .perf-card-sub[_ngcontent-%COMP%] {\n font-size: 11px;\n color: var(--%NS%prism-text-muted);\n margin-top: 4px;\n font-family: 'JetBrains Mono', monospace;\n }\n\n .perf-detail[_ngcontent-%COMP%] { padding: 0 24px 20px; }\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 .perf-sparkline-hl[_ngcontent-%COMP%] { color: var(--%NS%prism-primary); }\n .perf-sparkline-wrap[_ngcontent-%COMP%] { margin-top: 10px; position: relative; }\n\n .perf-empty[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n height: 100%;\n min-height: 180px;\n }\n .perf-empty-text[_ngcontent-%COMP%] { font-size: 12px; color: var(--%NS%prism-text-ghost); }\n .perf-empty-hint[_ngcontent-%COMP%] { font-size: 11px; color: var(--%NS%prism-text-ghost); opacity: 0.6; }"] });
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}"] });
150
150
  }
151
151
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(RenderSectionComponent, [{
152
152
  type: Component,
153
- args: [{ selector: 'perf-render-section', standalone: true, imports: [SparklineComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
154
- @if (renderService(); as svc) {
155
- @if (svc.initialRender() !== null || svc.rerenders().length > 0) {
156
- <div class="perf-body">
157
- <div class="perf-card">
158
- <div class="perf-card-head"><span class="perf-card-name">Initial Render</span></div>
159
- <div class="perf-card-val" [style.color]="colorFor(svc.initialRender())">{{ formatMs(svc.initialRender()) }}<span>ms</span></div>
160
- </div>
161
- <div class="perf-card">
162
- <div class="perf-card-head"><span class="perf-card-name">Avg Re-Render</span></div>
163
- <div class="perf-card-val">{{ formatMs(svc.avgRerender()) }}<span>ms</span></div>
164
- <div class="perf-card-sub">{{ svc.sampleCount() }} samples</div>
165
- </div>
166
- <div class="perf-card">
167
- <div class="perf-card-head"><span class="perf-card-name">Slowest</span></div>
168
- <div class="perf-card-val">{{ formatMs(svc.maxRerender()) }}<span>ms</span></div>
169
- </div>
170
- <div class="perf-card">
171
- <div class="perf-card-head"><span class="perf-card-name">P95</span></div>
172
- <div class="perf-card-val">{{ formatMs(svc.p95Rerender()) }}<span>ms</span></div>
173
- </div>
174
- <div class="perf-card">
175
- <div class="perf-card-head"><span class="perf-card-name">Last Render</span></div>
176
- <div class="perf-card-val">{{ formatMs(svc.lastRerender()) }}<span>ms</span></div>
177
- </div>
178
- </div>
179
-
180
- @if (svc.rerenders().length > 1) {
181
- <div class="perf-detail">
182
- <div class="perf-card perf-card--block">
183
- <div class="perf-card-head">
184
- <span class="perf-card-name">Render Time Trace &mdash; Last {{ svc.sampleCount() }} Samples</span>
185
- <span class="perf-sparkline-legend">avg <span class="perf-sparkline-hl">{{ formatMs(svc.avgRerender()) }} ms</span> &middot; max <span class="perf-sparkline-hl">{{ formatMs(svc.maxRerender()) }} ms</span></span>
186
- </div>
187
- <div class="perf-sparkline-wrap">
188
- <perf-sparkline [samples]="svc.rerenders()" [thresholdWarn]="warnMs()" [height]="80" />
189
- </div>
190
- </div>
191
- </div>
192
- }
193
- } @else {
194
- <div class="perf-empty">
195
- <div class="perf-empty-text">No profiling data yet</div>
196
- <div class="perf-empty-hint">Click Profile to start recording render times</div>
197
- </div>
198
- }
199
- }
200
- `, styles: ["\n .perf-body {\n padding: 20px 24px;\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 16px;\n align-content: start;\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 .perf-card--block { display: block; }\n .perf-card-head {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n }\n .perf-card-name {\n font-size: 11px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--prism-text-ghost);\n font-weight: 700;\n }\n .perf-card-val {\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 display: flex;\n align-items: baseline;\n gap: 4px;\n }\n .perf-card-val span {\n font-size: 13px;\n color: var(--prism-text-muted);\n font-weight: 500;\n }\n .perf-card-sub {\n font-size: 11px;\n color: var(--prism-text-muted);\n margin-top: 4px;\n font-family: 'JetBrains Mono', monospace;\n }\n\n .perf-detail { padding: 0 24px 20px; }\n\n .perf-sparkline-legend {\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n color: var(--prism-text-muted);\n }\n .perf-sparkline-hl { color: var(--prism-primary); }\n .perf-sparkline-wrap { margin-top: 10px; position: relative; }\n\n .perf-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n height: 100%;\n min-height: 180px;\n }\n .perf-empty-text { font-size: 12px; color: var(--prism-text-ghost); }\n .perf-empty-hint { font-size: 11px; color: var(--prism-text-ghost); opacity: 0.6; }\n "] }]
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"] }]
201
154
  }], 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 }] }] }); })();
202
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RenderSectionComponent, { className: "RenderSectionComponent", filePath: "render/render-section.component.ts", lineNumber: 132 }); })();
155
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(RenderSectionComponent, { className: "RenderSectionComponent", filePath: "render/render-section.component.ts", lineNumber: 13 }); })();
@@ -1 +1 @@
1
- {"version":3,"file":"sparkline.component.d.ts","sourceRoot":"","sources":["../../src/render/sparkline.component.ts"],"names":[],"mappings":";AAEA,qBA+Da,kBAAkB;IAC7B,QAAQ,CAAC,OAAO,gDAAuB;IACvC,QAAQ,CAAC,aAAa,8CAAY;IAClC,QAAQ,CAAC,MAAM,8CAAa;IAE5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAapB;IAEH,QAAQ,CAAC,QAAQ,yCAId;IAEH,QAAQ,CAAC,QAAQ,yCAMd;IAEH,QAAQ,CAAC,KAAK,gDAMX;IAEH,QAAQ,CAAC,SAAS;;;;cAQf;IAEH,QAAQ,CAAC,YAAY;;;;cAGlB;yCAvDQ,kBAAkB;2CAAlB,kBAAkB;CAwD9B"}
1
+ {"version":3,"file":"sparkline.component.d.ts","sourceRoot":"","sources":["../../src/render/sparkline.component.ts"],"names":[],"mappings":";AAEA,qBAOa,kBAAkB;IAC3B,QAAQ,CAAC,OAAO,gDAAuB;IACvC,QAAQ,CAAC,aAAa,8CAAY;IAClC,QAAQ,CAAC,MAAM,8CAAa;IAE5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAcpB;IAEH,QAAQ,CAAC,QAAQ,yCAKd;IAEH,QAAQ,CAAC,QAAQ,yCAQd;IAEH,QAAQ,CAAC,KAAK,gDAQX;IAEH,QAAQ,CAAC,SAAS;;;;cAUf;IAEH,QAAQ,CAAC,YAAY;;;;cAIlB;yCAhEM,kBAAkB;2CAAlB,kBAAkB;CAiE9B"}
@@ -12,7 +12,7 @@ function SparklineComponent_Conditional_11_Template(rf, ctx) { if (rf & 1) {
12
12
  i0.ɵɵadvance();
13
13
  i0.ɵɵattribute("x", 3)("y", ctx_r0.warnY() - 3);
14
14
  i0.ɵɵadvance();
15
- i0.ɵɵtextInterpolate1("", ctx_r0.thresholdWarn(), "ms");
15
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.thresholdWarn(), "ms ");
16
16
  } }
17
17
  function SparklineComponent_Conditional_14_Template(rf, ctx) { if (rf & 1) {
18
18
  i0.ɵɵnamespaceSVG();
@@ -49,8 +49,8 @@ export class SparklineComponent {
49
49
  const step = data.length > 1 ? 600 / (data.length - 1) : 300;
50
50
  return data.map((v, i) => ({
51
51
  x: i * step,
52
- y: h - 4 - ((v / max) * (h - 8)),
53
- value: v,
52
+ y: h - 4 - (v / max) * (h - 8),
53
+ value: v
54
54
  }));
55
55
  }, /* @ts-ignore */
56
56
  ...(ngDevMode ? [{ debugName: "points" }] : /* istanbul ignore next */ []));
@@ -76,7 +76,7 @@ export class SparklineComponent {
76
76
  if (data.length === 0)
77
77
  return null;
78
78
  const max = Math.max(...data, this.thresholdWarn() * 1.2);
79
- return h - 4 - ((this.thresholdWarn() / max) * (h - 8));
79
+ return h - 4 - (this.thresholdWarn() / max) * (h - 8);
80
80
  }, /* @ts-ignore */
81
81
  ...(ngDevMode ? [{ debugName: "warnY" }] : /* istanbul ignore next */ []));
82
82
  peakPoint = computed(() => {
@@ -128,47 +128,10 @@ export class SparklineComponent {
128
128
  i0.ɵɵconditional((tmp_4_0 = ctx.peakPoint()) ? 14 : -1, tmp_4_0);
129
129
  i0.ɵɵadvance();
130
130
  i0.ɵɵconditional((tmp_5_0 = ctx.currentPoint()) ? 15 : -1, tmp_5_0);
131
- } }, styles: [".prism-perf-sparkline-wrap[_ngcontent-%COMP%] { position: relative; }\n .prism-perf-sparkline-svg[_ngcontent-%COMP%] { width: 100%; height: 52px; }\n\n .sparkline-gradient-start[_ngcontent-%COMP%] { stop-color: var(--%NS%prism-primary); stop-opacity: 0.35; }\n .sparkline-gradient-end[_ngcontent-%COMP%] { stop-color: var(--%NS%prism-primary); stop-opacity: 0; }\n\n .sparkline-warn-line[_ngcontent-%COMP%] {\n stroke: var(--%NS%prism-warn);\n stroke-width: 0.8;\n stroke-dasharray: 4 4;\n opacity: 0.4;\n }\n .sparkline-warn-text[_ngcontent-%COMP%] { fill: var(--%NS%prism-warn); opacity: 0.5; }\n\n .sparkline-line[_ngcontent-%COMP%] { stroke: var(--%NS%prism-primary); }\n .sparkline-peak[_ngcontent-%COMP%] { fill: var(--%NS%prism-warn); }\n .sparkline-current[_ngcontent-%COMP%] { fill: var(--%NS%prism-primary); }\n .sparkline-current-pulse[_ngcontent-%COMP%] { fill: none; stroke: var(--%NS%prism-primary); opacity: 0.4; }"] });
131
+ } }, styles: [".prism-perf-sparkline-wrap[_ngcontent-%COMP%] {\n position: relative;\n}\n\n.prism-perf-sparkline-svg[_ngcontent-%COMP%] {\n width: 100%;\n height: 52px;\n}\n\n.sparkline-gradient-start[_ngcontent-%COMP%] {\n stop-color: var(--%NS%prism-primary);\n stop-opacity: 0.35;\n}\n\n.sparkline-gradient-end[_ngcontent-%COMP%] {\n stop-color: var(--%NS%prism-primary);\n stop-opacity: 0;\n}\n\n.sparkline-warn-line[_ngcontent-%COMP%] {\n opacity: 0.4;\n stroke: var(--%NS%prism-warn);\n stroke-width: 0.8;\n stroke-dasharray: 4 4;\n}\n\n.sparkline-warn-text[_ngcontent-%COMP%] {\n opacity: 0.5;\n fill: var(--%NS%prism-warn);\n}\n\n.sparkline-line[_ngcontent-%COMP%] {\n stroke: var(--%NS%prism-primary);\n}\n\n.sparkline-peak[_ngcontent-%COMP%] {\n fill: var(--%NS%prism-warn);\n}\n\n.sparkline-current[_ngcontent-%COMP%] {\n fill: var(--%NS%prism-primary);\n}\n\n.sparkline-current-pulse[_ngcontent-%COMP%] {\n opacity: 0.4;\n fill: none;\n stroke: var(--%NS%prism-primary);\n}"] });
132
132
  }
133
133
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SparklineComponent, [{
134
134
  type: Component,
135
- args: [{ selector: 'perf-sparkline', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
136
- <div class="prism-perf-sparkline-wrap">
137
- <svg class="prism-perf-sparkline-svg" [attr.viewBox]="'0 0 600 ' + height()" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg">
138
- <defs>
139
- <linearGradient id="perf-sparkg" x1="0" y1="0" x2="0" y2="1">
140
- <stop offset="0%" class="sparkline-gradient-start"/>
141
- <stop offset="100%" class="sparkline-gradient-end"/>
142
- </linearGradient>
143
- <filter id="perf-glow">
144
- <feGaussianBlur stdDeviation="1.5" result="coloredBlur"/>
145
- <feMerge><feMergeNode in="coloredBlur"/><feMergeNode in="SourceGraphic"/></feMerge>
146
- </filter>
147
- </defs>
148
-
149
- @if (warnY() !== null) {
150
- <line [attr.x1]="0" [attr.y1]="warnY()" [attr.x2]="600" [attr.y2]="warnY()"
151
- class="sparkline-warn-line"/>
152
- <text [attr.x]="3" [attr.y]="warnY()! - 3" font-size="8" class="sparkline-warn-text"
153
- font-family="monospace">{{ thresholdWarn() }}ms</text>
154
- }
155
-
156
- <path [attr.d]="areaPath()" fill="url(#perf-sparkg)"/>
157
- <path [attr.d]="linePath()" fill="none" class="sparkline-line" stroke-width="1.5" filter="url(#perf-glow)"/>
158
-
159
- @if (peakPoint(); as peak) {
160
- <circle [attr.cx]="peak.x" [attr.cy]="peak.y" r="3" class="sparkline-peak" filter="url(#perf-glow)"/>
161
- }
162
-
163
- @if (currentPoint(); as current) {
164
- <circle [attr.cx]="current.x" [attr.cy]="current.y" r="2.5" class="sparkline-current"/>
165
- <circle [attr.cx]="current.x" [attr.cy]="current.y" r="5" class="sparkline-current-pulse" stroke-width="1">
166
- <animate attributeName="r" values="3;7" dur="1.5s" repeatCount="indefinite"/>
167
- <animate attributeName="opacity" values="0.4;0" dur="1.5s" repeatCount="indefinite"/>
168
- </circle>
169
- }
170
- </svg>
171
- </div>
172
- `, styles: ["\n .prism-perf-sparkline-wrap { position: relative; }\n .prism-perf-sparkline-svg { width: 100%; height: 52px; }\n\n .sparkline-gradient-start { stop-color: var(--prism-primary); stop-opacity: 0.35; }\n .sparkline-gradient-end { stop-color: var(--prism-primary); stop-opacity: 0; }\n\n .sparkline-warn-line {\n stroke: var(--prism-warn);\n stroke-width: 0.8;\n stroke-dasharray: 4 4;\n opacity: 0.4;\n }\n .sparkline-warn-text { fill: var(--prism-warn); opacity: 0.5; }\n\n .sparkline-line { stroke: var(--prism-primary); }\n .sparkline-peak { fill: var(--prism-warn); }\n .sparkline-current { fill: var(--prism-primary); }\n .sparkline-current-pulse { fill: none; stroke: var(--prism-primary); opacity: 0.4; }\n "] }]
135
+ args: [{ selector: 'perf-sparkline', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"prism-perf-sparkline-wrap\">\n <svg\n class=\"prism-perf-sparkline-svg\"\n [attr.viewBox]=\"'0 0 600 ' + height()\"\n preserveAspectRatio=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\">\n <defs>\n <linearGradient\n id=\"perf-sparkg\"\n x1=\"0\"\n y1=\"0\"\n x2=\"0\"\n y2=\"1\">\n <stop\n offset=\"0%\"\n class=\"sparkline-gradient-start\" />\n <stop\n offset=\"100%\"\n class=\"sparkline-gradient-end\" />\n </linearGradient>\n <filter id=\"perf-glow\">\n <feGaussianBlur\n stdDeviation=\"1.5\"\n result=\"coloredBlur\" />\n <feMerge>\n <feMergeNode in=\"coloredBlur\" />\n <feMergeNode in=\"SourceGraphic\" />\n </feMerge>\n </filter>\n </defs>\n\n @if (warnY() !== null) {\n <line\n [attr.x1]=\"0\"\n [attr.y1]=\"warnY()\"\n [attr.x2]=\"600\"\n [attr.y2]=\"warnY()\"\n class=\"sparkline-warn-line\" />\n <text\n [attr.x]=\"3\"\n [attr.y]=\"warnY()! - 3\"\n font-size=\"8\"\n class=\"sparkline-warn-text\"\n font-family=\"monospace\">\n {{ thresholdWarn() }}ms\n </text>\n }\n\n <path\n [attr.d]=\"areaPath()\"\n fill=\"url(#perf-sparkg)\" />\n <path\n [attr.d]=\"linePath()\"\n fill=\"none\"\n class=\"sparkline-line\"\n stroke-width=\"1.5\"\n filter=\"url(#perf-glow)\" />\n\n @if (peakPoint(); as peak) {\n <circle\n [attr.cx]=\"peak.x\"\n [attr.cy]=\"peak.y\"\n r=\"3\"\n class=\"sparkline-peak\"\n filter=\"url(#perf-glow)\" />\n }\n\n @if (currentPoint(); as current) {\n <circle\n [attr.cx]=\"current.x\"\n [attr.cy]=\"current.y\"\n r=\"2.5\"\n class=\"sparkline-current\" />\n <circle\n [attr.cx]=\"current.x\"\n [attr.cy]=\"current.y\"\n r=\"5\"\n class=\"sparkline-current-pulse\"\n stroke-width=\"1\">\n <animate\n attributeName=\"r\"\n values=\"3;7\"\n dur=\"1.5s\"\n repeatCount=\"indefinite\" />\n <animate\n attributeName=\"opacity\"\n values=\"0.4;0\"\n dur=\"1.5s\"\n repeatCount=\"indefinite\" />\n </circle>\n }\n </svg>\n</div>\n", styles: [".prism-perf-sparkline-wrap {\n position: relative;\n}\n\n.prism-perf-sparkline-svg {\n width: 100%;\n height: 52px;\n}\n\n.sparkline-gradient-start {\n stop-color: var(--prism-primary);\n stop-opacity: 0.35;\n}\n\n.sparkline-gradient-end {\n stop-color: var(--prism-primary);\n stop-opacity: 0;\n}\n\n.sparkline-warn-line {\n opacity: 0.4;\n stroke: var(--prism-warn);\n stroke-width: 0.8;\n stroke-dasharray: 4 4;\n}\n\n.sparkline-warn-text {\n opacity: 0.5;\n fill: var(--prism-warn);\n}\n\n.sparkline-line {\n stroke: var(--prism-primary);\n}\n\n.sparkline-peak {\n fill: var(--prism-warn);\n}\n\n.sparkline-current {\n fill: var(--prism-primary);\n}\n\n.sparkline-current-pulse {\n opacity: 0.4;\n fill: none;\n stroke: var(--prism-primary);\n}\n"] }]
173
136
  }], null, { samples: [{ type: i0.Input, args: [{ isSignal: true, alias: "samples", required: false }] }], thresholdWarn: [{ type: i0.Input, args: [{ isSignal: true, alias: "thresholdWarn", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }] }); })();
174
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SparklineComponent, { className: "SparklineComponent", filePath: "render/sparkline.component.ts", lineNumber: 66 }); })();
137
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SparklineComponent, { className: "SparklineComponent", filePath: "render/sparkline.component.ts", lineNumber: 10 }); })();
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/schematics/ng-add/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,IAAI,EAGV,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAyBtD,wBAAgB,KAAK,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAEvD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/schematics/ng-add/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,KAAK,IAAI,EAAoC,MAAM,4BAA4B,CAAC;AAEhG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAwBtD,wBAAgB,KAAK,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAEvD"}
@@ -1,4 +1,4 @@
1
- import { chain, } from '@angular-devkit/schematics';
1
+ import { chain } from '@angular-devkit/schematics';
2
2
  import { addPluginToConfig } from '@ng-prism/core/schematics/utils';
3
3
  const PLUGIN_IMPORT_NAME = 'perfPlugin';
4
4
  const PLUGIN_IMPORT_FROM = '@ng-prism/plugin-perf';
@@ -9,7 +9,7 @@ function injectPlugin(options) {
9
9
  const changed = addPluginToConfig(tree, configPath, {
10
10
  importName: PLUGIN_IMPORT_NAME,
11
11
  importFrom: PLUGIN_IMPORT_FROM,
12
- call: PLUGIN_CALL,
12
+ call: PLUGIN_CALL
13
13
  });
14
14
  if (changed) {
15
15
  context.logger.info(` ${PLUGIN_IMPORT_FROM} → added to ${configPath}`);
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/schematics/ng-add/schema.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/schematics/ng-add/schema.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,kBAAkB;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-prism/plugin-perf",
3
- "version": "22.2.0",
3
+ "version": "22.3.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,8 @@
36
36
  "peerDependencies": {
37
37
  "@angular/core": ">=20.0.0",
38
38
  "typescript": ">=5.0.0",
39
- "@ng-prism/core": "^22.2.0-0"
39
+ "@ng-prism/core": "^22.3.0-0",
40
+ "@angular-devkit/schematics": ">=20.0.0"
40
41
  },
41
42
  "devDependencies": {
42
43
  "@ng-prism/core": "*"
@@ -1,10 +1,10 @@
1
1
  {
2
- "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
3
- "schematics": {
4
- "ng-add": {
5
- "description": "Adds @ng-prism/plugin-perf to the ng-prism config",
6
- "factory": "../dist/schematics/ng-add/index#ngAdd",
7
- "schema": "./ng-add/schema.json"
2
+ "$schema": "../node_modules/@angular-devkit/schematics/collection-schema.json",
3
+ "schematics": {
4
+ "ng-add": {
5
+ "description": "Adds @ng-prism/plugin-perf to the ng-prism config",
6
+ "factory": "../dist/schematics/ng-add/index#ngAdd",
7
+ "schema": "./ng-add/schema.json"
8
+ }
8
9
  }
9
- }
10
10
  }
@@ -1,16 +1,16 @@
1
1
  {
2
- "$schema": "http://json-schema.org/draft-07/schema#",
3
- "$id": "NgPrismPluginPerfNgAdd",
4
- "title": "Add @ng-prism/plugin-perf",
5
- "description": "Registers @ng-prism/plugin-perf in ng-prism.config.ts",
6
- "type": "object",
7
- "properties": {
8
- "configPath": {
9
- "type": "string",
10
- "description": "Path to ng-prism.config.ts",
11
- "default": "ng-prism.config.ts"
12
- }
13
- },
14
- "required": [],
15
- "additionalProperties": false
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "NgPrismPluginPerfNgAdd",
4
+ "title": "Add @ng-prism/plugin-perf",
5
+ "description": "Registers @ng-prism/plugin-perf in ng-prism.config.ts",
6
+ "type": "object",
7
+ "properties": {
8
+ "configPath": {
9
+ "type": "string",
10
+ "description": "Path to ng-prism.config.ts",
11
+ "default": "ng-prism.config.ts"
12
+ }
13
+ },
14
+ "required": [],
15
+ "additionalProperties": false
16
16
  }