@ng-prism/plugin-perf 21.0.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.
- package/dist/bundle/bundle-scanner.d.ts +3 -0
- package/dist/bundle/bundle-scanner.d.ts.map +1 -0
- package/dist/bundle/bundle-scanner.js +85 -0
- package/dist/bundle/bundle-section.component.d.ts +23 -0
- package/dist/bundle/bundle-section.component.d.ts.map +1 -0
- package/dist/bundle/bundle-section.component.js +321 -0
- package/dist/index.browser.d.ts +5 -0
- package/dist/index.browser.d.ts.map +1 -0
- package/dist/index.browser.js +3 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/memory/memory-section.component.d.ts +14 -0
- package/dist/memory/memory-section.component.d.ts.map +1 -0
- package/dist/memory/memory-section.component.js +323 -0
- package/dist/memory/perf-memory.service.d.ts +26 -0
- package/dist/memory/perf-memory.service.d.ts.map +1 -0
- package/dist/memory/perf-memory.service.js +62 -0
- package/dist/perf-config.token.d.ts +4 -0
- package/dist/perf-config.token.d.ts.map +1 -0
- package/dist/perf-config.token.js +2 -0
- package/dist/perf-panel.component.d.ts +18 -0
- package/dist/perf-panel.component.d.ts.map +1 -0
- package/dist/perf-panel.component.js +247 -0
- package/dist/perf-plugin.browser.d.ts +4 -0
- package/dist/perf-plugin.browser.d.ts.map +1 -0
- package/dist/perf-plugin.browser.js +14 -0
- package/dist/perf-plugin.d.ts +4 -0
- package/dist/perf-plugin.d.ts.map +1 -0
- package/dist/perf-plugin.js +27 -0
- package/dist/perf.types.d.ts +31 -0
- package/dist/perf.types.d.ts.map +1 -0
- package/dist/perf.types.js +8 -0
- package/dist/render/perf-render.service.d.ts +19 -0
- package/dist/render/perf-render.service.d.ts.map +1 -0
- package/dist/render/perf-render.service.js +73 -0
- package/dist/render/render-section.component.d.ts +13 -0
- package/dist/render/render-section.component.d.ts.map +1 -0
- package/dist/render/render-section.component.js +205 -0
- package/dist/render/sparkline.component.d.ts +23 -0
- package/dist/render/sparkline.component.d.ts.map +1 -0
- package/dist/render/sparkline.component.js +165 -0
- package/package.json +49 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, computed, input, signal } from '@angular/core';
|
|
2
|
+
import { DEFAULT_THRESHOLDS } from './perf.types.js';
|
|
3
|
+
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
|
+
import { RenderSectionComponent } from './render/render-section.component.js';
|
|
7
|
+
import { MemorySectionComponent } from './memory/memory-section.component.js';
|
|
8
|
+
import * as i0 from "@angular/core";
|
|
9
|
+
function PerfPanelComponent_Conditional_14_Template(rf, ctx) { if (rf & 1) {
|
|
10
|
+
i0.ɵɵelementStart(0, "div", 4);
|
|
11
|
+
i0.ɵɵtext(1);
|
|
12
|
+
i0.ɵɵelementEnd();
|
|
13
|
+
} if (rf & 2) {
|
|
14
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
15
|
+
i0.ɵɵadvance();
|
|
16
|
+
i0.ɵɵtextInterpolate1("", ctx_r0.renderService.cdRunCount(), " CD runs");
|
|
17
|
+
} }
|
|
18
|
+
function PerfPanelComponent_Conditional_15_Conditional_1_Template(rf, ctx) { if (rf & 1) {
|
|
19
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
20
|
+
i0.ɵɵelementStart(0, "button", 15);
|
|
21
|
+
i0.ɵɵlistener("click", function PerfPanelComponent_Conditional_15_Conditional_1_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.renderService.stopRecording()); });
|
|
22
|
+
i0.ɵɵelementStart(1, "i", 12);
|
|
23
|
+
i0.ɵɵtext(2, "\u23F8");
|
|
24
|
+
i0.ɵɵelementEnd();
|
|
25
|
+
i0.ɵɵtext(3, " Pause ");
|
|
26
|
+
i0.ɵɵelementEnd();
|
|
27
|
+
} }
|
|
28
|
+
function PerfPanelComponent_Conditional_15_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
29
|
+
const _r4 = i0.ɵɵgetCurrentView();
|
|
30
|
+
i0.ɵɵelementStart(0, "button", 15);
|
|
31
|
+
i0.ɵɵlistener("click", function PerfPanelComponent_Conditional_15_Conditional_2_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.renderService.startRecording()); });
|
|
32
|
+
i0.ɵɵelementStart(1, "i", 12);
|
|
33
|
+
i0.ɵɵtext(2, "\u25B6");
|
|
34
|
+
i0.ɵɵelementEnd();
|
|
35
|
+
i0.ɵɵtext(3, " Profile ");
|
|
36
|
+
i0.ɵɵelementEnd();
|
|
37
|
+
} }
|
|
38
|
+
function PerfPanelComponent_Conditional_15_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
39
|
+
i0.ɵɵelementStart(0, "div", 14);
|
|
40
|
+
i0.ɵɵelement(1, "div", 16);
|
|
41
|
+
i0.ɵɵtext(2);
|
|
42
|
+
i0.ɵɵelementEnd();
|
|
43
|
+
} if (rf & 2) {
|
|
44
|
+
const ctx_r0 = i0.ɵɵnextContext(2);
|
|
45
|
+
i0.ɵɵadvance(2);
|
|
46
|
+
i0.ɵɵtextInterpolate1(" live \u00B7 ", ctx_r0.renderService.sampleCount(), " samples ");
|
|
47
|
+
} }
|
|
48
|
+
function PerfPanelComponent_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
49
|
+
const _r2 = i0.ɵɵgetCurrentView();
|
|
50
|
+
i0.ɵɵelementStart(0, "div", 5);
|
|
51
|
+
i0.ɵɵconditionalCreate(1, PerfPanelComponent_Conditional_15_Conditional_1_Template, 4, 0, "button", 10)(2, PerfPanelComponent_Conditional_15_Conditional_2_Template, 4, 0, "button", 10);
|
|
52
|
+
i0.ɵɵelementStart(3, "button", 11);
|
|
53
|
+
i0.ɵɵlistener("click", function PerfPanelComponent_Conditional_15_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.renderService.clear()); });
|
|
54
|
+
i0.ɵɵelementStart(4, "i", 12);
|
|
55
|
+
i0.ɵɵtext(5, "\u2715");
|
|
56
|
+
i0.ɵɵelementEnd();
|
|
57
|
+
i0.ɵɵtext(6, " Clear ");
|
|
58
|
+
i0.ɵɵelementEnd();
|
|
59
|
+
i0.ɵɵelement(7, "div", 13);
|
|
60
|
+
i0.ɵɵconditionalCreate(8, PerfPanelComponent_Conditional_15_Conditional_8_Template, 3, 1, "div", 14);
|
|
61
|
+
i0.ɵɵelementEnd();
|
|
62
|
+
} if (rf & 2) {
|
|
63
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
64
|
+
i0.ɵɵadvance();
|
|
65
|
+
i0.ɵɵconditional(ctx_r0.renderService.isRecording() ? 1 : 2);
|
|
66
|
+
i0.ɵɵadvance(7);
|
|
67
|
+
i0.ɵɵconditional(ctx_r0.renderService.isRecording() ? 8 : -1);
|
|
68
|
+
} }
|
|
69
|
+
function PerfPanelComponent_Conditional_16_Template(rf, ctx) { if (rf & 1) {
|
|
70
|
+
const _r5 = i0.ɵɵgetCurrentView();
|
|
71
|
+
i0.ɵɵelementStart(0, "div", 5)(1, "button", 15);
|
|
72
|
+
i0.ɵɵlistener("click", function PerfPanelComponent_Conditional_16_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r5); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.captureMemorySnapshots()); });
|
|
73
|
+
i0.ɵɵelementStart(2, "i", 12);
|
|
74
|
+
i0.ɵɵtext(3, "\u25B6");
|
|
75
|
+
i0.ɵɵelementEnd();
|
|
76
|
+
i0.ɵɵtext(4, " Snapshot ");
|
|
77
|
+
i0.ɵɵelementEnd();
|
|
78
|
+
i0.ɵɵelementStart(5, "button", 11);
|
|
79
|
+
i0.ɵɵlistener("click", function PerfPanelComponent_Conditional_16_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r5); const ctx_r0 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r0.memoryService.clear()); });
|
|
80
|
+
i0.ɵɵelementStart(6, "i", 12);
|
|
81
|
+
i0.ɵɵtext(7, "\u2715");
|
|
82
|
+
i0.ɵɵelementEnd();
|
|
83
|
+
i0.ɵɵtext(8, " Clear ");
|
|
84
|
+
i0.ɵɵelementEnd();
|
|
85
|
+
i0.ɵɵelement(9, "div", 13);
|
|
86
|
+
i0.ɵɵelementEnd();
|
|
87
|
+
} }
|
|
88
|
+
function PerfPanelComponent_Case_18_Template(rf, ctx) { if (rf & 1) {
|
|
89
|
+
i0.ɵɵelement(0, "perf-bundle-section", 7);
|
|
90
|
+
} if (rf & 2) {
|
|
91
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
92
|
+
i0.ɵɵproperty("metrics", ctx_r0.bundleMetrics())("warnKb", ctx_r0.thresholds.bundleWarnKb)("critKb", ctx_r0.thresholds.bundleCritKb);
|
|
93
|
+
} }
|
|
94
|
+
function PerfPanelComponent_Case_19_Template(rf, ctx) { if (rf & 1) {
|
|
95
|
+
i0.ɵɵelement(0, "perf-render-section", 8);
|
|
96
|
+
} if (rf & 2) {
|
|
97
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
98
|
+
i0.ɵɵproperty("renderService", ctx_r0.renderService)("warnMs", ctx_r0.thresholds.renderWarnMs)("critMs", ctx_r0.thresholds.renderCritMs);
|
|
99
|
+
} }
|
|
100
|
+
function PerfPanelComponent_Case_20_Template(rf, ctx) { if (rf & 1) {
|
|
101
|
+
i0.ɵɵelement(0, "perf-memory-section", 9);
|
|
102
|
+
} if (rf & 2) {
|
|
103
|
+
const ctx_r0 = i0.ɵɵnextContext();
|
|
104
|
+
i0.ɵɵproperty("memoryService", ctx_r0.memoryService);
|
|
105
|
+
} }
|
|
106
|
+
export class PerfPanelComponent {
|
|
107
|
+
activeComponent = input(null, ...(ngDevMode ? [{ debugName: "activeComponent" }] : []));
|
|
108
|
+
activeTab = signal('bundle', ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
|
|
109
|
+
renderService = PerfRenderService.getInstance();
|
|
110
|
+
memoryService = PerfMemoryService.getInstance();
|
|
111
|
+
thresholds = DEFAULT_THRESHOLDS;
|
|
112
|
+
captureMemorySnapshots() {
|
|
113
|
+
this.memoryService.takeSnapshot('before-create');
|
|
114
|
+
setTimeout(() => this.memoryService.takeSnapshot('after-create'), 50);
|
|
115
|
+
}
|
|
116
|
+
bundleMetrics = computed(() => {
|
|
117
|
+
const comp = this.activeComponent();
|
|
118
|
+
return comp?.meta?.showcaseConfig?.meta?.perf?.bundle ?? null;
|
|
119
|
+
}, ...(ngDevMode ? [{ debugName: "bundleMetrics" }] : []));
|
|
120
|
+
static ɵfac = function PerfPanelComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PerfPanelComponent)(); };
|
|
121
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PerfPanelComponent, selectors: [["prism-perf-panel"]], inputs: { activeComponent: [1, "activeComponent"] }, decls: 21, vars: 10, consts: [[1, "prism-perf-panel"], [1, "prism-perf-subtabs"], [1, "prism-perf-subtab", 3, "click"], [1, "prism-perf-subtab-icon"], [1, "prism-perf-cd-badge"], [1, "prism-perf-toolbar"], [1, "prism-perf-content"], [3, "metrics", "warnKb", "critKb"], [3, "renderService", "warnMs", "critMs"], [3, "memoryService"], [1, "prism-perf-btn", "prism-perf-btn--primary"], [1, "prism-perf-btn", 3, "click"], [1, "prism-perf-btn-icon"], [1, "prism-perf-toolbar-spacer"], [1, "prism-perf-status"], [1, "prism-perf-btn", "prism-perf-btn--primary", 3, "click"], [1, "prism-perf-status-dot"]], template: function PerfPanelComponent_Template(rf, ctx) { if (rf & 1) {
|
|
122
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "button", 2);
|
|
123
|
+
i0.ɵɵlistener("click", function PerfPanelComponent_Template_button_click_2_listener() { return ctx.activeTab.set("bundle"); });
|
|
124
|
+
i0.ɵɵelementStart(3, "span", 3);
|
|
125
|
+
i0.ɵɵtext(4, "\uD83D\uDCE6");
|
|
126
|
+
i0.ɵɵelementEnd();
|
|
127
|
+
i0.ɵɵtext(5, " Bundle ");
|
|
128
|
+
i0.ɵɵelementEnd();
|
|
129
|
+
i0.ɵɵelementStart(6, "button", 2);
|
|
130
|
+
i0.ɵɵlistener("click", function PerfPanelComponent_Template_button_click_6_listener() { return ctx.activeTab.set("render"); });
|
|
131
|
+
i0.ɵɵelementStart(7, "span", 3);
|
|
132
|
+
i0.ɵɵtext(8, "\u23F1");
|
|
133
|
+
i0.ɵɵelementEnd();
|
|
134
|
+
i0.ɵɵtext(9, " Render ");
|
|
135
|
+
i0.ɵɵelementEnd();
|
|
136
|
+
i0.ɵɵelementStart(10, "button", 2);
|
|
137
|
+
i0.ɵɵlistener("click", function PerfPanelComponent_Template_button_click_10_listener() { return ctx.activeTab.set("memory"); });
|
|
138
|
+
i0.ɵɵelementStart(11, "span", 3);
|
|
139
|
+
i0.ɵɵtext(12, "\uD83E\uDDE0");
|
|
140
|
+
i0.ɵɵelementEnd();
|
|
141
|
+
i0.ɵɵtext(13, " Memory ");
|
|
142
|
+
i0.ɵɵelementEnd();
|
|
143
|
+
i0.ɵɵconditionalCreate(14, PerfPanelComponent_Conditional_14_Template, 2, 1, "div", 4);
|
|
144
|
+
i0.ɵɵelementEnd();
|
|
145
|
+
i0.ɵɵconditionalCreate(15, PerfPanelComponent_Conditional_15_Template, 9, 2, "div", 5);
|
|
146
|
+
i0.ɵɵconditionalCreate(16, PerfPanelComponent_Conditional_16_Template, 10, 0, "div", 5);
|
|
147
|
+
i0.ɵɵelementStart(17, "div", 6);
|
|
148
|
+
i0.ɵɵconditionalCreate(18, PerfPanelComponent_Case_18_Template, 1, 3, "perf-bundle-section", 7)(19, PerfPanelComponent_Case_19_Template, 1, 3, "perf-render-section", 8)(20, PerfPanelComponent_Case_20_Template, 1, 1, "perf-memory-section", 9);
|
|
149
|
+
i0.ɵɵelementEnd()();
|
|
150
|
+
} if (rf & 2) {
|
|
151
|
+
let tmp_6_0;
|
|
152
|
+
i0.ɵɵadvance(2);
|
|
153
|
+
i0.ɵɵclassProp("active", ctx.activeTab() === "bundle");
|
|
154
|
+
i0.ɵɵadvance(4);
|
|
155
|
+
i0.ɵɵclassProp("active", ctx.activeTab() === "render");
|
|
156
|
+
i0.ɵɵadvance(4);
|
|
157
|
+
i0.ɵɵclassProp("active", ctx.activeTab() === "memory");
|
|
158
|
+
i0.ɵɵadvance(4);
|
|
159
|
+
i0.ɵɵconditional(ctx.renderService.cdRunCount() > 0 ? 14 : -1);
|
|
160
|
+
i0.ɵɵadvance();
|
|
161
|
+
i0.ɵɵconditional(ctx.activeTab() === "render" ? 15 : -1);
|
|
162
|
+
i0.ɵɵadvance();
|
|
163
|
+
i0.ɵɵconditional(ctx.activeTab() === "memory" ? 16 : -1);
|
|
164
|
+
i0.ɵɵadvance(2);
|
|
165
|
+
i0.ɵɵconditional((tmp_6_0 = ctx.activeTab()) === "bundle" ? 18 : tmp_6_0 === "render" ? 19 : tmp_6_0 === "memory" ? 20 : -1);
|
|
166
|
+
} }, dependencies: [BundleSectionComponent, RenderSectionComponent, MemorySectionComponent], styles: [".prism-perf-panel[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n height: 340px;\n }\n .prism-perf-subtabs[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n background: var(--prism-bg-elevated, #1a1535);\n border-bottom: 1px solid var(--prism-border, rgba(255,255,255,0.08));\n padding: 0 4px;\n }\n .prism-perf-subtab[_ngcontent-%COMP%] {\n padding: 7px 12px;\n font-size: 11px;\n font-family: var(--prism-font-sans, system-ui);\n font-weight: 500;\n color: var(--prism-text-muted, #8476a2);\n border: none;\n background: none;\n cursor: pointer;\n position: relative;\n display: flex;\n align-items: center;\n gap: 5px;\n transition: color 0.12s;\n letter-spacing: 0.01em;\n user-select: none;\n }\n .prism-perf-subtab[_ngcontent-%COMP%]::after {\n content: '';\n position: absolute;\n bottom: -1px; left: 4px; right: 4px;\n height: 2px;\n background: var(--prism-primary, #a78bfa);\n opacity: 0;\n }\n .prism-perf-subtab.active[_ngcontent-%COMP%] { color: var(--prism-primary, #a78bfa); }\n .prism-perf-subtab.active[_ngcontent-%COMP%]::after { opacity: 1; }\n .prism-perf-subtab-icon[_ngcontent-%COMP%] { font-size: 10px; opacity: 0.8; }\n .prism-perf-cd-badge[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n border-radius: 3px;\n background: color-mix(in srgb, var(--prism-primary, #a78bfa) 12%, transparent);\n border: 1px solid color-mix(in srgb, var(--prism-primary, #a78bfa) 20%, transparent);\n font-size: 10px;\n font-family: var(--prism-font-mono, monospace);\n color: var(--prism-primary, #a78bfa);\n margin-left: auto;\n }\n .prism-perf-toolbar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border-bottom: 1px solid var(--prism-border, rgba(255,255,255,0.08));\n background: var(--prism-bg-surface, #131022);\n flex-shrink: 0;\n }\n .prism-perf-btn[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 5px;\n padding: 4px 10px;\n border: 1px solid var(--prism-border-strong, rgba(255,255,255,0.16));\n border-radius: 5px;\n background: var(--prism-bg-elevated, #1a1535);\n color: var(--prism-text-2, #b0a6c8);\n font-family: var(--prism-font-sans, system-ui);\n font-size: 11px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.1s, border-color 0.1s, color 0.1s;\n user-select: none;\n }\n .prism-perf-btn[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--prism-primary, #a78bfa) 12%, var(--prism-bg-elevated, #1a1535));\n border-color: color-mix(in srgb, var(--prism-primary, #a78bfa) 40%, transparent);\n color: var(--prism-primary, #a78bfa);\n }\n .prism-perf-btn--primary[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--prism-primary, #a78bfa) 15%, var(--prism-bg-elevated, #1a1535));\n border-color: color-mix(in srgb, var(--prism-primary, #a78bfa) 35%, transparent);\n color: var(--prism-primary, #a78bfa);\n }\n .prism-perf-btn--primary[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--prism-primary, #a78bfa) 22%, var(--prism-bg-elevated, #1a1535));\n }\n .prism-perf-btn-icon[_ngcontent-%COMP%] { font-style: normal; font-size: 10px; }\n .prism-perf-toolbar-spacer[_ngcontent-%COMP%] { flex: 1; }\n .prism-perf-status[_ngcontent-%COMP%] {\n font-size: 10px;\n color: var(--prism-text-ghost, #6a5d87);\n font-family: var(--prism-font-mono, monospace);\n display: flex;\n align-items: center;\n gap: 5px;\n }\n .prism-perf-status-dot[_ngcontent-%COMP%] {\n width: 5px; height: 5px;\n border-radius: 50%;\n background: var(--perf-ok, #4ade80);\n box-shadow: 0 0 6px var(--perf-ok, #4ade80);\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 .prism-perf-content[_ngcontent-%COMP%] {\n flex: 1;\n overflow-y: auto;\n background: var(--prism-bg-surface, #131022);\n scrollbar-width: thin;\n scrollbar-color: var(--prism-border, rgba(255,255,255,0.08)) transparent;\n }"], changeDetection: 0 });
|
|
167
|
+
}
|
|
168
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PerfPanelComponent, [{
|
|
169
|
+
type: Component,
|
|
170
|
+
args: [{ selector: 'prism-perf-panel', standalone: true, imports: [BundleSectionComponent, RenderSectionComponent, MemorySectionComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
171
|
+
<div class="prism-perf-panel">
|
|
172
|
+
<div class="prism-perf-subtabs">
|
|
173
|
+
<button class="prism-perf-subtab" [class.active]="activeTab() === 'bundle'" (click)="activeTab.set('bundle')">
|
|
174
|
+
<span class="prism-perf-subtab-icon">📦</span> Bundle
|
|
175
|
+
</button>
|
|
176
|
+
<button class="prism-perf-subtab" [class.active]="activeTab() === 'render'" (click)="activeTab.set('render')">
|
|
177
|
+
<span class="prism-perf-subtab-icon">⏱</span> Render
|
|
178
|
+
</button>
|
|
179
|
+
<button class="prism-perf-subtab" [class.active]="activeTab() === 'memory'" (click)="activeTab.set('memory')">
|
|
180
|
+
<span class="prism-perf-subtab-icon">🧠</span> Memory
|
|
181
|
+
</button>
|
|
182
|
+
@if (renderService.cdRunCount() > 0) {
|
|
183
|
+
<div class="prism-perf-cd-badge">{{ renderService.cdRunCount() }} CD runs</div>
|
|
184
|
+
}
|
|
185
|
+
</div>
|
|
186
|
+
|
|
187
|
+
@if (activeTab() === 'render') {
|
|
188
|
+
<div class="prism-perf-toolbar">
|
|
189
|
+
@if (renderService.isRecording()) {
|
|
190
|
+
<button class="prism-perf-btn prism-perf-btn--primary" (click)="renderService.stopRecording()">
|
|
191
|
+
<i class="prism-perf-btn-icon">⏸</i> Pause
|
|
192
|
+
</button>
|
|
193
|
+
} @else {
|
|
194
|
+
<button class="prism-perf-btn prism-perf-btn--primary" (click)="renderService.startRecording()">
|
|
195
|
+
<i class="prism-perf-btn-icon">▶</i> Profile
|
|
196
|
+
</button>
|
|
197
|
+
}
|
|
198
|
+
<button class="prism-perf-btn" (click)="renderService.clear()">
|
|
199
|
+
<i class="prism-perf-btn-icon">✕</i> Clear
|
|
200
|
+
</button>
|
|
201
|
+
<div class="prism-perf-toolbar-spacer"></div>
|
|
202
|
+
@if (renderService.isRecording()) {
|
|
203
|
+
<div class="prism-perf-status">
|
|
204
|
+
<div class="prism-perf-status-dot"></div>
|
|
205
|
+
live · {{ renderService.sampleCount() }} samples
|
|
206
|
+
</div>
|
|
207
|
+
}
|
|
208
|
+
</div>
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
@if (activeTab() === 'memory') {
|
|
212
|
+
<div class="prism-perf-toolbar">
|
|
213
|
+
<button class="prism-perf-btn prism-perf-btn--primary" (click)="captureMemorySnapshots()">
|
|
214
|
+
<i class="prism-perf-btn-icon">▶</i> Snapshot
|
|
215
|
+
</button>
|
|
216
|
+
<button class="prism-perf-btn" (click)="memoryService.clear()">
|
|
217
|
+
<i class="prism-perf-btn-icon">✕</i> Clear
|
|
218
|
+
</button>
|
|
219
|
+
<div class="prism-perf-toolbar-spacer"></div>
|
|
220
|
+
</div>
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
<div class="prism-perf-content">
|
|
224
|
+
@switch (activeTab()) {
|
|
225
|
+
@case ('bundle') {
|
|
226
|
+
<perf-bundle-section
|
|
227
|
+
[metrics]="bundleMetrics()"
|
|
228
|
+
[warnKb]="thresholds.bundleWarnKb"
|
|
229
|
+
[critKb]="thresholds.bundleCritKb"
|
|
230
|
+
/>
|
|
231
|
+
}
|
|
232
|
+
@case ('render') {
|
|
233
|
+
<perf-render-section
|
|
234
|
+
[renderService]="renderService"
|
|
235
|
+
[warnMs]="thresholds.renderWarnMs"
|
|
236
|
+
[critMs]="thresholds.renderCritMs"
|
|
237
|
+
/>
|
|
238
|
+
}
|
|
239
|
+
@case ('memory') {
|
|
240
|
+
<perf-memory-section [memoryService]="memoryService" />
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
</div>
|
|
244
|
+
</div>
|
|
245
|
+
`, styles: ["\n .prism-perf-panel {\n display: flex;\n flex-direction: column;\n height: 340px;\n }\n .prism-perf-subtabs {\n display: flex;\n align-items: center;\n background: var(--prism-bg-elevated, #1a1535);\n border-bottom: 1px solid var(--prism-border, rgba(255,255,255,0.08));\n padding: 0 4px;\n }\n .prism-perf-subtab {\n padding: 7px 12px;\n font-size: 11px;\n font-family: var(--prism-font-sans, system-ui);\n font-weight: 500;\n color: var(--prism-text-muted, #8476a2);\n border: none;\n background: none;\n cursor: pointer;\n position: relative;\n display: flex;\n align-items: center;\n gap: 5px;\n transition: color 0.12s;\n letter-spacing: 0.01em;\n user-select: none;\n }\n .prism-perf-subtab::after {\n content: '';\n position: absolute;\n bottom: -1px; left: 4px; right: 4px;\n height: 2px;\n background: var(--prism-primary, #a78bfa);\n opacity: 0;\n }\n .prism-perf-subtab.active { color: var(--prism-primary, #a78bfa); }\n .prism-perf-subtab.active::after { opacity: 1; }\n .prism-perf-subtab-icon { font-size: 10px; opacity: 0.8; }\n .prism-perf-cd-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 8px;\n border-radius: 3px;\n background: color-mix(in srgb, var(--prism-primary, #a78bfa) 12%, transparent);\n border: 1px solid color-mix(in srgb, var(--prism-primary, #a78bfa) 20%, transparent);\n font-size: 10px;\n font-family: var(--prism-font-mono, monospace);\n color: var(--prism-primary, #a78bfa);\n margin-left: auto;\n }\n .prism-perf-toolbar {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border-bottom: 1px solid var(--prism-border, rgba(255,255,255,0.08));\n background: var(--prism-bg-surface, #131022);\n flex-shrink: 0;\n }\n .prism-perf-btn {\n display: flex;\n align-items: center;\n gap: 5px;\n padding: 4px 10px;\n border: 1px solid var(--prism-border-strong, rgba(255,255,255,0.16));\n border-radius: 5px;\n background: var(--prism-bg-elevated, #1a1535);\n color: var(--prism-text-2, #b0a6c8);\n font-family: var(--prism-font-sans, system-ui);\n font-size: 11px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.1s, border-color 0.1s, color 0.1s;\n user-select: none;\n }\n .prism-perf-btn:hover {\n background: color-mix(in srgb, var(--prism-primary, #a78bfa) 12%, var(--prism-bg-elevated, #1a1535));\n border-color: color-mix(in srgb, var(--prism-primary, #a78bfa) 40%, transparent);\n color: var(--prism-primary, #a78bfa);\n }\n .prism-perf-btn--primary {\n background: color-mix(in srgb, var(--prism-primary, #a78bfa) 15%, var(--prism-bg-elevated, #1a1535));\n border-color: color-mix(in srgb, var(--prism-primary, #a78bfa) 35%, transparent);\n color: var(--prism-primary, #a78bfa);\n }\n .prism-perf-btn--primary:hover {\n background: color-mix(in srgb, var(--prism-primary, #a78bfa) 22%, var(--prism-bg-elevated, #1a1535));\n }\n .prism-perf-btn-icon { font-style: normal; font-size: 10px; }\n .prism-perf-toolbar-spacer { flex: 1; }\n .prism-perf-status {\n font-size: 10px;\n color: var(--prism-text-ghost, #6a5d87);\n font-family: var(--prism-font-mono, monospace);\n display: flex;\n align-items: center;\n gap: 5px;\n }\n .prism-perf-status-dot {\n width: 5px; height: 5px;\n border-radius: 50%;\n background: var(--perf-ok, #4ade80);\n box-shadow: 0 0 6px var(--perf-ok, #4ade80);\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 .prism-perf-content {\n flex: 1;\n overflow-y: auto;\n background: var(--prism-bg-surface, #131022);\n scrollbar-width: thin;\n scrollbar-color: var(--prism-border, rgba(255,255,255,0.08)) transparent;\n }\n "] }]
|
|
246
|
+
}], null, { activeComponent: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeComponent", required: false }] }] }); })();
|
|
247
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PerfPanelComponent, { className: "PerfPanelComponent", filePath: "perf-panel.component.ts", lineNumber: 215 }); })();
|
|
@@ -0,0 +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,CActE"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function perfPlugin(_options) {
|
|
2
|
+
return {
|
|
3
|
+
name: '@ng-prism/plugin-perf',
|
|
4
|
+
panels: [
|
|
5
|
+
{
|
|
6
|
+
id: 'perf',
|
|
7
|
+
label: 'Performance',
|
|
8
|
+
loadComponent: () => import('./perf-panel.component.js').then((m) => m.PerfPanelComponent),
|
|
9
|
+
position: 'bottom',
|
|
10
|
+
placement: 'view',
|
|
11
|
+
},
|
|
12
|
+
],
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { scanBundle } from './bundle/bundle-scanner.js';
|
|
2
|
+
export function perfPlugin(options) {
|
|
3
|
+
return {
|
|
4
|
+
name: '@ng-prism/plugin-perf',
|
|
5
|
+
async onComponentScanned(component) {
|
|
6
|
+
const bundle = scanBundle(component.filePath, options?.bundle?.maxTreeDepth ?? 5);
|
|
7
|
+
return {
|
|
8
|
+
...component,
|
|
9
|
+
showcaseConfig: {
|
|
10
|
+
...component.showcaseConfig,
|
|
11
|
+
meta: {
|
|
12
|
+
...component.showcaseConfig.meta,
|
|
13
|
+
perf: { bundle },
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
},
|
|
18
|
+
panels: [
|
|
19
|
+
{
|
|
20
|
+
id: 'perf',
|
|
21
|
+
label: 'Performance',
|
|
22
|
+
loadComponent: () => import('./perf-panel.component.js').then((m) => m.PerfPanelComponent),
|
|
23
|
+
position: 'bottom',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface BundleMetrics {
|
|
2
|
+
sourceSize: number;
|
|
3
|
+
gzipEstimate: number;
|
|
4
|
+
directImports: number;
|
|
5
|
+
importList: string[];
|
|
6
|
+
treeDepth: number;
|
|
7
|
+
}
|
|
8
|
+
export interface PerfThresholds {
|
|
9
|
+
bundleWarnKb: number;
|
|
10
|
+
bundleCritKb: number;
|
|
11
|
+
renderWarnMs: number;
|
|
12
|
+
renderCritMs: number;
|
|
13
|
+
memoryWarnMb: number;
|
|
14
|
+
memoryLeakMb: number;
|
|
15
|
+
}
|
|
16
|
+
export interface PerfPluginOptions {
|
|
17
|
+
thresholds?: Partial<PerfThresholds>;
|
|
18
|
+
bundle?: {
|
|
19
|
+
maxTreeDepth?: number;
|
|
20
|
+
excludeImports?: string[];
|
|
21
|
+
};
|
|
22
|
+
render?: {
|
|
23
|
+
bufferSize?: number;
|
|
24
|
+
autoStart?: boolean;
|
|
25
|
+
};
|
|
26
|
+
memory?: {
|
|
27
|
+
gcDelayMs?: number;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export declare const DEFAULT_THRESHOLDS: PerfThresholds;
|
|
31
|
+
//# sourceMappingURL=perf.types.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export declare class PerfRenderService {
|
|
2
|
+
static getInstance(): PerfRenderService;
|
|
3
|
+
readonly isRecording: import("@angular/core").WritableSignal<boolean>;
|
|
4
|
+
readonly initialRender: import("@angular/core").WritableSignal<number | null>;
|
|
5
|
+
readonly rerenders: import("@angular/core").WritableSignal<number[]>;
|
|
6
|
+
readonly cdRunCount: import("@angular/core").WritableSignal<number>;
|
|
7
|
+
private observer;
|
|
8
|
+
private bufferSize;
|
|
9
|
+
readonly avgRerender: import("@angular/core").Signal<number>;
|
|
10
|
+
readonly p95Rerender: import("@angular/core").Signal<number>;
|
|
11
|
+
readonly maxRerender: import("@angular/core").Signal<number>;
|
|
12
|
+
readonly lastRerender: import("@angular/core").Signal<number | null>;
|
|
13
|
+
readonly sampleCount: import("@angular/core").Signal<number>;
|
|
14
|
+
configure(bufferSize: number): void;
|
|
15
|
+
startRecording(): void;
|
|
16
|
+
stopRecording(): void;
|
|
17
|
+
clear(): void;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=perf-render.service.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { signal, computed } from '@angular/core';
|
|
2
|
+
let instance = null;
|
|
3
|
+
export class PerfRenderService {
|
|
4
|
+
static getInstance() {
|
|
5
|
+
if (!instance)
|
|
6
|
+
instance = new PerfRenderService();
|
|
7
|
+
return instance;
|
|
8
|
+
}
|
|
9
|
+
isRecording = signal(false, ...(ngDevMode ? [{ debugName: "isRecording" }] : []));
|
|
10
|
+
initialRender = signal(null, ...(ngDevMode ? [{ debugName: "initialRender" }] : []));
|
|
11
|
+
rerenders = signal([], ...(ngDevMode ? [{ debugName: "rerenders" }] : []));
|
|
12
|
+
cdRunCount = signal(0, ...(ngDevMode ? [{ debugName: "cdRunCount" }] : []));
|
|
13
|
+
observer = null;
|
|
14
|
+
bufferSize = 50;
|
|
15
|
+
avgRerender = computed(() => {
|
|
16
|
+
const samples = this.rerenders();
|
|
17
|
+
if (samples.length === 0)
|
|
18
|
+
return 0;
|
|
19
|
+
return samples.reduce((a, b) => a + b, 0) / samples.length;
|
|
20
|
+
}, ...(ngDevMode ? [{ debugName: "avgRerender" }] : []));
|
|
21
|
+
p95Rerender = computed(() => {
|
|
22
|
+
const samples = this.rerenders();
|
|
23
|
+
if (samples.length === 0)
|
|
24
|
+
return 0;
|
|
25
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
26
|
+
const idx = Math.ceil(sorted.length * 0.95) - 1;
|
|
27
|
+
return sorted[idx];
|
|
28
|
+
}, ...(ngDevMode ? [{ debugName: "p95Rerender" }] : []));
|
|
29
|
+
maxRerender = computed(() => {
|
|
30
|
+
const samples = this.rerenders();
|
|
31
|
+
if (samples.length === 0)
|
|
32
|
+
return 0;
|
|
33
|
+
return Math.max(...samples);
|
|
34
|
+
}, ...(ngDevMode ? [{ debugName: "maxRerender" }] : []));
|
|
35
|
+
lastRerender = computed(() => {
|
|
36
|
+
const samples = this.rerenders();
|
|
37
|
+
return samples.length > 0 ? samples[samples.length - 1] : null;
|
|
38
|
+
}, ...(ngDevMode ? [{ debugName: "lastRerender" }] : []));
|
|
39
|
+
sampleCount = computed(() => this.rerenders().length, ...(ngDevMode ? [{ debugName: "sampleCount" }] : []));
|
|
40
|
+
configure(bufferSize) {
|
|
41
|
+
this.bufferSize = bufferSize;
|
|
42
|
+
}
|
|
43
|
+
startRecording() {
|
|
44
|
+
if (this.isRecording())
|
|
45
|
+
return;
|
|
46
|
+
this.isRecording.set(true);
|
|
47
|
+
this.observer = new PerformanceObserver((list) => {
|
|
48
|
+
for (const entry of list.getEntries()) {
|
|
49
|
+
if (entry.name === 'prism:render') {
|
|
50
|
+
this.initialRender.set(entry.duration);
|
|
51
|
+
}
|
|
52
|
+
else if (entry.name === 'prism:rerender') {
|
|
53
|
+
this.cdRunCount.update((c) => c + 1);
|
|
54
|
+
this.rerenders.update((arr) => {
|
|
55
|
+
const next = [...arr, entry.duration];
|
|
56
|
+
return next.length > this.bufferSize ? next.slice(-this.bufferSize) : next;
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
this.observer.observe({ type: 'measure', buffered: true });
|
|
62
|
+
}
|
|
63
|
+
stopRecording() {
|
|
64
|
+
this.isRecording.set(false);
|
|
65
|
+
this.observer?.disconnect();
|
|
66
|
+
this.observer = null;
|
|
67
|
+
}
|
|
68
|
+
clear() {
|
|
69
|
+
this.initialRender.set(null);
|
|
70
|
+
this.rerenders.set([]);
|
|
71
|
+
this.cdRunCount.set(0);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { PerfRenderService } from './perf-render.service.js';
|
|
2
|
+
import * as i0 from "@angular/core";
|
|
3
|
+
export declare class RenderSectionComponent {
|
|
4
|
+
readonly renderService: import("@angular/core").InputSignal<PerfRenderService>;
|
|
5
|
+
readonly warnMs: import("@angular/core").InputSignal<number>;
|
|
6
|
+
readonly critMs: import("@angular/core").InputSignal<number>;
|
|
7
|
+
readonly initialClass: import("@angular/core").Signal<"warn" | "" | "crit">;
|
|
8
|
+
formatMs(value: number | null): string;
|
|
9
|
+
colorFor(value: number): string;
|
|
10
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<RenderSectionComponent, never>;
|
|
11
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<RenderSectionComponent, "perf-render-section", never, { "renderService": { "alias": "renderService"; "required": true; "isSignal": true; }; "warnMs": { "alias": "warnMs"; "required": false; "isSignal": true; }; "critMs": { "alias": "critMs"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=render-section.component.d.ts.map
|
|
@@ -0,0 +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,qBAsJa,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,MAAM;yCAlBpB,sBAAsB;2CAAtB,sBAAsB;CAuBlC"}
|