@ng-prism/core 21.12.0-beta.0 → 21.12.0-beta.1

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 (62) hide show
  1. package/dist/app/header/prism-header-widget-host.component.d.ts +11 -0
  2. package/dist/app/header/prism-header-widget-host.component.d.ts.map +1 -0
  3. package/dist/app/header/prism-header-widget-host.component.js +57 -0
  4. package/dist/app/header/prism-header.component.d.ts +3 -0
  5. package/dist/app/header/prism-header.component.d.ts.map +1 -1
  6. package/dist/app/header/prism-header.component.js +76 -20
  7. package/dist/app/panels/a11y/a11y-header-badge.component.d.ts +16 -0
  8. package/dist/app/panels/a11y/a11y-header-badge.component.d.ts.map +1 -0
  9. package/dist/app/panels/a11y/a11y-header-badge.component.js +84 -0
  10. package/dist/app/panels/a11y/a11y-thresholds.d.ts +6 -0
  11. package/dist/app/panels/a11y/a11y-thresholds.d.ts.map +1 -0
  12. package/dist/app/panels/a11y/a11y-thresholds.js +13 -0
  13. package/dist/app/panels/a11y/a11y.types.d.ts +33 -0
  14. package/dist/app/panels/a11y/a11y.types.d.ts.map +1 -1
  15. package/dist/app/panels/builtin-header-widgets.d.ts +3 -0
  16. package/dist/app/panels/builtin-header-widgets.d.ts.map +1 -0
  17. package/dist/app/panels/builtin-header-widgets.js +9 -0
  18. package/dist/app/panels/panel-host/prism-panel-host.component.d.ts +16 -5
  19. package/dist/app/panels/panel-host/prism-panel-host.component.d.ts.map +1 -1
  20. package/dist/app/panels/panel-host/prism-panel-host.component.js +91 -44
  21. package/dist/app/provide-prism.d.ts.map +1 -1
  22. package/dist/app/provide-prism.js +11 -0
  23. package/dist/app/renderer/prism-renderer.component.d.ts +2 -0
  24. package/dist/app/renderer/prism-renderer.component.d.ts.map +1 -1
  25. package/dist/app/renderer/prism-renderer.component.js +18 -3
  26. package/dist/app/services/prism-panel.service.d.ts +5 -0
  27. package/dist/app/services/prism-panel.service.d.ts.map +1 -1
  28. package/dist/app/services/prism-panel.service.js +7 -3
  29. package/dist/app/services/prism-plugin.service.d.ts +4 -1
  30. package/dist/app/services/prism-plugin.service.d.ts.map +1 -1
  31. package/dist/app/services/prism-plugin.service.js +11 -0
  32. package/dist/builder/a11y/a11y-report-reader.d.ts +12 -0
  33. package/dist/builder/a11y/a11y-report-reader.d.ts.map +1 -0
  34. package/dist/builder/a11y/a11y-report-reader.js +49 -0
  35. package/dist/builder/manifest/runtime-manifest.generator.d.ts +1 -0
  36. package/dist/builder/manifest/runtime-manifest.generator.d.ts.map +1 -1
  37. package/dist/builder/manifest/runtime-manifest.generator.js +8 -1
  38. package/dist/builder/plugin-runner/plugin-runner.d.ts.map +1 -1
  39. package/dist/builder/plugin-runner/plugin-runner.js +3 -1
  40. package/dist/builder/shared/prism-pipeline.d.ts.map +1 -1
  41. package/dist/builder/shared/prism-pipeline.js +19 -1
  42. package/dist/cli/audit-a11y/aggregator.d.ts +10 -0
  43. package/dist/cli/audit-a11y/aggregator.d.ts.map +1 -0
  44. package/dist/cli/audit-a11y/aggregator.js +64 -0
  45. package/dist/cli/audit-a11y/auditor.d.ts +14 -0
  46. package/dist/cli/audit-a11y/auditor.d.ts.map +1 -0
  47. package/dist/cli/audit-a11y/auditor.js +104 -0
  48. package/dist/cli/audit-a11y/cli.d.ts +3 -0
  49. package/dist/cli/audit-a11y/cli.d.ts.map +1 -0
  50. package/dist/cli/audit-a11y/cli.js +143 -0
  51. package/dist/cli/audit-a11y/index.d.ts +4 -0
  52. package/dist/cli/audit-a11y/index.d.ts.map +1 -0
  53. package/dist/cli/audit-a11y/index.js +3 -0
  54. package/dist/cli/audit-a11y/static-server.d.ts +11 -0
  55. package/dist/cli/audit-a11y/static-server.d.ts.map +1 -0
  56. package/dist/cli/audit-a11y/static-server.js +61 -0
  57. package/dist/plugin/index.d.ts +2 -2
  58. package/dist/plugin/index.d.ts.map +1 -1
  59. package/dist/plugin/index.js +1 -1
  60. package/dist/plugin/plugin.types.d.ts +33 -0
  61. package/dist/plugin/plugin.types.d.ts.map +1 -1
  62. package/package.json +12 -3
@@ -1,5 +1,5 @@
1
1
  import { NgComponentOutlet } from '@angular/common';
2
- import { Component, computed, effect, EnvironmentInjector, inject, signal, ChangeDetectionStrategy, } from '@angular/core';
2
+ import { Component, computed, effect, EnvironmentInjector, inject, signal, ChangeDetectionStrategy, untracked, } from '@angular/core';
3
3
  import { PrismIconComponent } from '../../icons/prism-icon.component.js';
4
4
  import { BUILTIN_PANELS } from '../builtin-panels.js';
5
5
  import { A11yAuditService } from '../a11y/a11y-audit.service.js';
@@ -10,13 +10,13 @@ import { PrismRendererService } from '../../services/prism-renderer.service.js';
10
10
  import * as i0 from "@angular/core";
11
11
  const _forTrack0 = ($index, $item) => $item.id;
12
12
  function PrismPanelHostComponent_For_3_Conditional_1_Template(rf, ctx) { if (rf & 1) {
13
- i0.ɵɵelement(0, "prism-icon", 5);
13
+ i0.ɵɵelement(0, "prism-icon", 6);
14
14
  } if (rf & 2) {
15
15
  const panel_r2 = i0.ɵɵnextContext().$implicit;
16
16
  i0.ɵɵproperty("name", panel_r2.icon)("size", 13);
17
17
  } }
18
18
  function PrismPanelHostComponent_For_3_Conditional_3_Template(rf, ctx) { if (rf & 1) {
19
- i0.ɵɵelementStart(0, "span", 7);
19
+ i0.ɵɵelementStart(0, "span", 8);
20
20
  i0.ɵɵtext(1);
21
21
  i0.ɵɵelementEnd();
22
22
  } if (rf & 2) {
@@ -27,11 +27,11 @@ function PrismPanelHostComponent_For_3_Conditional_3_Template(rf, ctx) { if (rf
27
27
  } }
28
28
  function PrismPanelHostComponent_For_3_Template(rf, ctx) { if (rf & 1) {
29
29
  const _r1 = i0.ɵɵgetCurrentView();
30
- i0.ɵɵelementStart(0, "button", 4);
30
+ i0.ɵɵelementStart(0, "button", 5);
31
31
  i0.ɵɵlistener("click", function PrismPanelHostComponent_For_3_Template_button_click_0_listener() { const panel_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.panelService.activePanelId.set(panel_r2.id)); });
32
- i0.ɵɵconditionalCreate(1, PrismPanelHostComponent_For_3_Conditional_1_Template, 1, 2, "prism-icon", 5);
32
+ i0.ɵɵconditionalCreate(1, PrismPanelHostComponent_For_3_Conditional_1_Template, 1, 2, "prism-icon", 6);
33
33
  i0.ɵɵtext(2);
34
- i0.ɵɵconditionalCreate(3, PrismPanelHostComponent_For_3_Conditional_3_Template, 2, 7, "span", 6);
34
+ i0.ɵɵconditionalCreate(3, PrismPanelHostComponent_For_3_Conditional_3_Template, 2, 7, "span", 7);
35
35
  i0.ɵɵelementEnd();
36
36
  } if (rf & 2) {
37
37
  let tmp_15_0;
@@ -46,14 +46,19 @@ function PrismPanelHostComponent_For_3_Template(rf, ctx) { if (rf & 1) {
46
46
  i0.ɵɵadvance();
47
47
  i0.ɵɵconditional((tmp_15_0 = ctx_r2.panelBadge(panel_r2.id)) ? 3 : -1, tmp_15_0);
48
48
  } }
49
- function PrismPanelHostComponent_Conditional_5_ng_container_0_Template(rf, ctx) { if (rf & 1) {
49
+ function PrismPanelHostComponent_For_6_ng_container_1_Template(rf, ctx) { if (rf & 1) {
50
50
  i0.ɵɵelementContainer(0);
51
51
  } }
52
- function PrismPanelHostComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
53
- i0.ɵɵtemplate(0, PrismPanelHostComponent_Conditional_5_ng_container_0_Template, 1, 0, "ng-container", 8);
52
+ function PrismPanelHostComponent_For_6_Template(rf, ctx) { if (rf & 1) {
53
+ i0.ɵɵelementStart(0, "div", 4);
54
+ i0.ɵɵtemplate(1, PrismPanelHostComponent_For_6_ng_container_1_Template, 1, 0, "ng-container", 9);
55
+ i0.ɵɵelementEnd();
54
56
  } if (rf & 2) {
57
+ const entry_r5 = ctx.$implicit;
55
58
  const ctx_r2 = i0.ɵɵnextContext();
56
- i0.ɵɵproperty("ngComponentOutlet", ctx_r2.resolvedComponent())("ngComponentOutletInputs", ctx_r2.panelInputs())("ngComponentOutletInjector", ctx_r2.activeInjector());
59
+ i0.ɵɵproperty("hidden", entry_r5.id !== ctx_r2.panelService.activePanelId());
60
+ i0.ɵɵadvance();
61
+ i0.ɵɵproperty("ngComponentOutlet", entry_r5.component)("ngComponentOutletInputs", ctx_r2.panelInputs())("ngComponentOutletInjector", entry_r5.injector ?? ctx_r2.envInjector);
57
62
  } }
58
63
  export class PrismPanelHostComponent {
59
64
  pluginService = inject(PrismPluginService);
@@ -110,12 +115,12 @@ export class PrismPanelHostComponent {
110
115
  return panels;
111
116
  return panels.filter((p) => !p.isVisible || p.isVisible(comp));
112
117
  }, ...(ngDevMode ? [{ debugName: "allPanels" }] : []));
113
- resolvedComponent = signal(null, ...(ngDevMode ? [{ debugName: "resolvedComponent" }] : []));
114
118
  panelInputs = computed(() => ({
115
119
  activeComponent: this.nav.activeComponent(),
116
120
  }), ...(ngDevMode ? [{ debugName: "panelInputs" }] : []));
117
121
  lazyCache = new Map();
118
- activeInjector = computed(() => this.panelService.activePanelInjector() ?? this.envInjector, ...(ngDevMode ? [{ debugName: "activeInjector" }] : []));
122
+ renderedPanels = signal(new Map(), ...(ngDevMode ? [{ debugName: "renderedPanels" }] : []));
123
+ renderedPanelsArray = computed(() => Array.from(this.renderedPanels().values()), ...(ngDevMode ? [{ debugName: "renderedPanelsArray" }] : []));
119
124
  constructor() {
120
125
  effect(() => {
121
126
  const element = this.rendererService.renderedElement();
@@ -134,36 +139,73 @@ export class PrismPanelHostComponent {
134
139
  this.auditService.scheduleAudit(element, a11yConfig);
135
140
  });
136
141
  effect(() => {
137
- const panel = this.allPanels().find((p) => p.id === this.panelService.activePanelId()) ?? null;
138
- if (!panel) {
139
- this.resolvedComponent.set(null);
140
- return;
141
- }
142
- if (panel.component) {
143
- this.resolvedComponent.set(panel.component);
142
+ const activeId = this.panelService.activePanelId();
143
+ const panels = this.allPanels();
144
+ untracked(() => {
145
+ const panel = panels.find((p) => p.id === activeId) ?? null;
146
+ if (panel) {
147
+ this.ensurePanelLoaded(panel);
148
+ }
149
+ this.pruneRenderedPanels(panels, panel ? activeId : null);
150
+ });
151
+ });
152
+ }
153
+ ensurePanelLoaded(panel) {
154
+ if (this.renderedPanels().has(panel.id))
155
+ return;
156
+ if (panel.component) {
157
+ this.addRenderedPanel(panel, panel.component);
158
+ return;
159
+ }
160
+ if (panel.loadComponent) {
161
+ const cached = this.lazyCache.get(panel.id);
162
+ if (cached) {
163
+ this.addRenderedPanel(panel, cached);
144
164
  return;
145
165
  }
146
- if (panel.loadComponent) {
147
- const cached = this.lazyCache.get(panel.id);
148
- if (cached) {
149
- this.resolvedComponent.set(cached);
166
+ panel.loadComponent().then((comp) => {
167
+ this.lazyCache.set(panel.id, comp);
168
+ if (this.renderedPanels().has(panel.id))
150
169
  return;
151
- }
152
- this.resolvedComponent.set(null);
153
- panel.loadComponent().then((comp) => {
154
- this.lazyCache.set(panel.id, comp);
155
- this.resolvedComponent.set(comp);
156
- });
157
- }
170
+ this.addRenderedPanel(panel, comp);
171
+ });
172
+ }
173
+ }
174
+ addRenderedPanel(panel, component) {
175
+ const next = new Map(this.renderedPanels());
176
+ next.set(panel.id, {
177
+ id: panel.id,
178
+ component,
179
+ injector: this.panelService.getInjector(panel.id),
180
+ keepAlive: panel.keepAlive === true,
158
181
  });
182
+ this.renderedPanels.set(next);
183
+ }
184
+ pruneRenderedPanels(visiblePanels, activeId) {
185
+ const current = this.renderedPanels();
186
+ if (current.size === 0)
187
+ return;
188
+ const visibleIds = new Set(visiblePanels.map((p) => p.id));
189
+ const next = new Map(current);
190
+ let changed = false;
191
+ for (const [id, entry] of current) {
192
+ const isVisible = visibleIds.has(id);
193
+ const isActive = id === activeId;
194
+ if (!isVisible || (!isActive && !entry.keepAlive)) {
195
+ next.delete(id);
196
+ changed = true;
197
+ }
198
+ }
199
+ if (changed)
200
+ this.renderedPanels.set(next);
159
201
  }
160
202
  static ɵfac = function PrismPanelHostComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PrismPanelHostComponent)(); };
161
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PrismPanelHostComponent, selectors: [["prism-panel-host"]], decls: 6, vars: 2, consts: [[1, "panel"], ["role", "tablist", 1, "panel-tabs"], ["role", "tab", 1, "p-tab", 3, "p-tab--active"], ["role", "tabpanel", 1, "panel-body", 3, "id"], ["role", "tab", 1, "p-tab", 3, "click"], [3, "name", "size"], [1, "p-tab-badge", 3, "ok", "warn", "danger"], [1, "p-tab-badge"], [4, "ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector"]], template: function PrismPanelHostComponent_Template(rf, ctx) { if (rf & 1) {
203
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PrismPanelHostComponent, selectors: [["prism-panel-host"]], decls: 7, vars: 1, consts: [[1, "panel"], ["role", "tablist", 1, "panel-tabs"], ["role", "tab", 1, "p-tab", 3, "p-tab--active"], ["role", "tabpanel", 1, "panel-body", 3, "id"], [1, "panel-pane", 3, "hidden"], ["role", "tab", 1, "p-tab", 3, "click"], [3, "name", "size"], [1, "p-tab-badge", 3, "ok", "warn", "danger"], [1, "p-tab-badge"], [4, "ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector"]], template: function PrismPanelHostComponent_Template(rf, ctx) { if (rf & 1) {
162
204
  i0.ɵɵelementStart(0, "div", 0)(1, "nav", 1);
163
205
  i0.ɵɵrepeaterCreate(2, PrismPanelHostComponent_For_3_Template, 4, 7, "button", 2, _forTrack0);
164
206
  i0.ɵɵelementEnd();
165
207
  i0.ɵɵelementStart(4, "div", 3);
166
- i0.ɵɵconditionalCreate(5, PrismPanelHostComponent_Conditional_5_Template, 1, 3, "ng-container");
208
+ i0.ɵɵrepeaterCreate(5, PrismPanelHostComponent_For_6_Template, 2, 4, "div", 4, _forTrack0);
167
209
  i0.ɵɵelementEnd()();
168
210
  } if (rf & 2) {
169
211
  i0.ɵɵadvance(2);
@@ -171,8 +213,8 @@ export class PrismPanelHostComponent {
171
213
  i0.ɵɵadvance(2);
172
214
  i0.ɵɵproperty("id", "panel-" + ctx.panelService.activePanelId());
173
215
  i0.ɵɵadvance();
174
- i0.ɵɵconditional(ctx.resolvedComponent() ? 5 : -1);
175
- } }, dependencies: [NgComponentOutlet, PrismIconComponent], styles: [".panel[_ngcontent-%COMP%] {\n background: var(--prism-bg-elevated);\n border-top: 1px solid var(--prism-border);\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow: hidden;\n height: 100%;\n }\n\n .panel-tabs[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 0 16px;\n height: 40px;\n border-bottom: 1px solid var(--prism-border);\n background: var(--prism-bg);\n overflow-x: auto;\n scrollbar-width: none;\n flex-shrink: 0;\n }\n .panel-tabs[_ngcontent-%COMP%]::-webkit-scrollbar { display: none; }\n\n .p-tab[_ngcontent-%COMP%] {\n position: relative;\n height: 100%;\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 0 12px;\n font-size: var(--fs-md);\n font-weight: 500;\n color: var(--prism-text-muted);\n white-space: nowrap;\n transition: color var(--dur-fast);\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n }\n .p-tab[_ngcontent-%COMP%]:hover { color: var(--prism-text-2); }\n .p-tab--active[_ngcontent-%COMP%] { color: var(--prism-text); }\n .p-tab--active[_ngcontent-%COMP%]::after {\n content: '';\n position: absolute;\n left: 8px;\n right: 8px;\n bottom: -1px;\n height: 2px;\n background: linear-gradient(90deg, var(--prism-primary-from), var(--prism-primary-to));\n border-radius: 1px;\n }\n\n .p-tab-badge[_ngcontent-%COMP%] {\n min-width: 16px;\n height: 16px;\n padding: 0 5px;\n border-radius: 8px;\n background: color-mix(in srgb, var(--prism-primary) 18%, transparent);\n color: var(--prism-primary);\n font-family: var(--font-mono);\n font-size: 10px;\n font-weight: 700;\n display: grid;\n place-items: center;\n }\n .p-tab-badge.ok[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--prism-success) 18%, transparent);\n color: var(--prism-success);\n }\n .p-tab-badge.warn[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--prism-warn) 18%, transparent);\n color: var(--prism-warn);\n }\n .p-tab-badge.danger[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--prism-danger) 18%, transparent);\n color: var(--prism-danger);\n }\n\n .panel-tabs-right[_ngcontent-%COMP%] {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 4px;\n padding-left: 10px;\n }\n\n .icon-btn[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\n display: grid;\n place-items: center;\n border-radius: var(--radius-sm);\n color: var(--prism-text-muted);\n background: none;\n border: none;\n cursor: pointer;\n transition: all var(--dur-fast);\n }\n .icon-btn[_ngcontent-%COMP%]:hover {\n background: var(--prism-input-bg);\n color: var(--prism-text);\n }\n\n .panel-body[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n overflow: auto;\n background: var(--prism-bg-elevated);\n }\n .panel-body[_ngcontent-%COMP%]::-webkit-scrollbar { width: 8px; height: 8px; }\n .panel-body[_ngcontent-%COMP%]::-webkit-scrollbar-thumb { background: var(--prism-border-strong); border-radius: 4px; }\n\n [_ngcontent-%COMP%]:focus-visible {\n outline: 2px solid var(--prism-primary);\n outline-offset: 2px;\n }"], changeDetection: 0 });
216
+ i0.ɵɵrepeater(ctx.renderedPanelsArray());
217
+ } }, dependencies: [NgComponentOutlet, PrismIconComponent], styles: [".panel[_ngcontent-%COMP%] {\n background: var(--prism-bg-elevated);\n border-top: 1px solid var(--prism-border);\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow: hidden;\n height: 100%;\n }\n\n .panel-tabs[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 0 16px;\n height: 40px;\n border-bottom: 1px solid var(--prism-border);\n background: var(--prism-bg);\n overflow-x: auto;\n scrollbar-width: none;\n flex-shrink: 0;\n }\n .panel-tabs[_ngcontent-%COMP%]::-webkit-scrollbar { display: none; }\n\n .p-tab[_ngcontent-%COMP%] {\n position: relative;\n height: 100%;\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 0 12px;\n font-size: var(--fs-md);\n font-weight: 500;\n color: var(--prism-text-muted);\n white-space: nowrap;\n transition: color var(--dur-fast);\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n }\n .p-tab[_ngcontent-%COMP%]:hover { color: var(--prism-text-2); }\n .p-tab--active[_ngcontent-%COMP%] { color: var(--prism-text); }\n .p-tab--active[_ngcontent-%COMP%]::after {\n content: '';\n position: absolute;\n left: 8px;\n right: 8px;\n bottom: -1px;\n height: 2px;\n background: linear-gradient(90deg, var(--prism-primary-from), var(--prism-primary-to));\n border-radius: 1px;\n }\n\n .p-tab-badge[_ngcontent-%COMP%] {\n min-width: 16px;\n height: 16px;\n padding: 0 5px;\n border-radius: 8px;\n background: color-mix(in srgb, var(--prism-primary) 18%, transparent);\n color: var(--prism-primary);\n font-family: var(--font-mono);\n font-size: 10px;\n font-weight: 700;\n display: grid;\n place-items: center;\n }\n .p-tab-badge.ok[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--prism-success) 18%, transparent);\n color: var(--prism-success);\n }\n .p-tab-badge.warn[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--prism-warn) 18%, transparent);\n color: var(--prism-warn);\n }\n .p-tab-badge.danger[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--prism-danger) 18%, transparent);\n color: var(--prism-danger);\n }\n\n .panel-tabs-right[_ngcontent-%COMP%] {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 4px;\n padding-left: 10px;\n }\n\n .icon-btn[_ngcontent-%COMP%] {\n width: 28px;\n height: 28px;\n display: grid;\n place-items: center;\n border-radius: var(--radius-sm);\n color: var(--prism-text-muted);\n background: none;\n border: none;\n cursor: pointer;\n transition: all var(--dur-fast);\n }\n .icon-btn[_ngcontent-%COMP%]:hover {\n background: var(--prism-input-bg);\n color: var(--prism-text);\n }\n\n .panel-body[_ngcontent-%COMP%] {\n flex: 1;\n min-height: 0;\n overflow: auto;\n background: var(--prism-bg-elevated);\n }\n .panel-body[_ngcontent-%COMP%]::-webkit-scrollbar { width: 8px; height: 8px; }\n .panel-body[_ngcontent-%COMP%]::-webkit-scrollbar-thumb { background: var(--prism-border-strong); border-radius: 4px; }\n\n .panel-pane[_ngcontent-%COMP%] { height: 100%; }\n .panel-pane[hidden][_ngcontent-%COMP%] { display: none; }\n\n [_ngcontent-%COMP%]:focus-visible {\n outline: 2px solid var(--prism-primary);\n outline-offset: 2px;\n }"], changeDetection: 0 });
176
218
  }
177
219
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PrismPanelHostComponent, [{
178
220
  type: Component,
@@ -213,17 +255,22 @@ export class PrismPanelHostComponent {
213
255
  [id]="'panel-' + panelService.activePanelId()"
214
256
  role="tabpanel"
215
257
  >
216
- @if (resolvedComponent()) {
217
- <ng-container
218
- *ngComponentOutlet="
219
- resolvedComponent();
220
- inputs: panelInputs();
221
- injector: activeInjector()
222
- "
223
- />
258
+ @for (entry of renderedPanelsArray(); track entry.id) {
259
+ <div
260
+ class="panel-pane"
261
+ [hidden]="entry.id !== panelService.activePanelId()"
262
+ >
263
+ <ng-container
264
+ *ngComponentOutlet="
265
+ entry.component;
266
+ inputs: panelInputs();
267
+ injector: entry.injector ?? envInjector
268
+ "
269
+ />
270
+ </div>
224
271
  }
225
272
  </div>
226
273
  </div>
227
- `, styles: ["\n .panel {\n background: var(--prism-bg-elevated);\n border-top: 1px solid var(--prism-border);\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow: hidden;\n height: 100%;\n }\n\n .panel-tabs {\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 0 16px;\n height: 40px;\n border-bottom: 1px solid var(--prism-border);\n background: var(--prism-bg);\n overflow-x: auto;\n scrollbar-width: none;\n flex-shrink: 0;\n }\n .panel-tabs::-webkit-scrollbar { display: none; }\n\n .p-tab {\n position: relative;\n height: 100%;\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 0 12px;\n font-size: var(--fs-md);\n font-weight: 500;\n color: var(--prism-text-muted);\n white-space: nowrap;\n transition: color var(--dur-fast);\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n }\n .p-tab:hover { color: var(--prism-text-2); }\n .p-tab--active { color: var(--prism-text); }\n .p-tab--active::after {\n content: '';\n position: absolute;\n left: 8px;\n right: 8px;\n bottom: -1px;\n height: 2px;\n background: linear-gradient(90deg, var(--prism-primary-from), var(--prism-primary-to));\n border-radius: 1px;\n }\n\n .p-tab-badge {\n min-width: 16px;\n height: 16px;\n padding: 0 5px;\n border-radius: 8px;\n background: color-mix(in srgb, var(--prism-primary) 18%, transparent);\n color: var(--prism-primary);\n font-family: var(--font-mono);\n font-size: 10px;\n font-weight: 700;\n display: grid;\n place-items: center;\n }\n .p-tab-badge.ok {\n background: color-mix(in srgb, var(--prism-success) 18%, transparent);\n color: var(--prism-success);\n }\n .p-tab-badge.warn {\n background: color-mix(in srgb, var(--prism-warn) 18%, transparent);\n color: var(--prism-warn);\n }\n .p-tab-badge.danger {\n background: color-mix(in srgb, var(--prism-danger) 18%, transparent);\n color: var(--prism-danger);\n }\n\n .panel-tabs-right {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 4px;\n padding-left: 10px;\n }\n\n .icon-btn {\n width: 28px;\n height: 28px;\n display: grid;\n place-items: center;\n border-radius: var(--radius-sm);\n color: var(--prism-text-muted);\n background: none;\n border: none;\n cursor: pointer;\n transition: all var(--dur-fast);\n }\n .icon-btn:hover {\n background: var(--prism-input-bg);\n color: var(--prism-text);\n }\n\n .panel-body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n background: var(--prism-bg-elevated);\n }\n .panel-body::-webkit-scrollbar { width: 8px; height: 8px; }\n .panel-body::-webkit-scrollbar-thumb { background: var(--prism-border-strong); border-radius: 4px; }\n\n :focus-visible {\n outline: 2px solid var(--prism-primary);\n outline-offset: 2px;\n }\n "] }]
274
+ `, styles: ["\n .panel {\n background: var(--prism-bg-elevated);\n border-top: 1px solid var(--prism-border);\n display: flex;\n flex-direction: column;\n min-height: 0;\n overflow: hidden;\n height: 100%;\n }\n\n .panel-tabs {\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 0 16px;\n height: 40px;\n border-bottom: 1px solid var(--prism-border);\n background: var(--prism-bg);\n overflow-x: auto;\n scrollbar-width: none;\n flex-shrink: 0;\n }\n .panel-tabs::-webkit-scrollbar { display: none; }\n\n .p-tab {\n position: relative;\n height: 100%;\n display: flex;\n align-items: center;\n gap: 7px;\n padding: 0 12px;\n font-size: var(--fs-md);\n font-weight: 500;\n color: var(--prism-text-muted);\n white-space: nowrap;\n transition: color var(--dur-fast);\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n }\n .p-tab:hover { color: var(--prism-text-2); }\n .p-tab--active { color: var(--prism-text); }\n .p-tab--active::after {\n content: '';\n position: absolute;\n left: 8px;\n right: 8px;\n bottom: -1px;\n height: 2px;\n background: linear-gradient(90deg, var(--prism-primary-from), var(--prism-primary-to));\n border-radius: 1px;\n }\n\n .p-tab-badge {\n min-width: 16px;\n height: 16px;\n padding: 0 5px;\n border-radius: 8px;\n background: color-mix(in srgb, var(--prism-primary) 18%, transparent);\n color: var(--prism-primary);\n font-family: var(--font-mono);\n font-size: 10px;\n font-weight: 700;\n display: grid;\n place-items: center;\n }\n .p-tab-badge.ok {\n background: color-mix(in srgb, var(--prism-success) 18%, transparent);\n color: var(--prism-success);\n }\n .p-tab-badge.warn {\n background: color-mix(in srgb, var(--prism-warn) 18%, transparent);\n color: var(--prism-warn);\n }\n .p-tab-badge.danger {\n background: color-mix(in srgb, var(--prism-danger) 18%, transparent);\n color: var(--prism-danger);\n }\n\n .panel-tabs-right {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 4px;\n padding-left: 10px;\n }\n\n .icon-btn {\n width: 28px;\n height: 28px;\n display: grid;\n place-items: center;\n border-radius: var(--radius-sm);\n color: var(--prism-text-muted);\n background: none;\n border: none;\n cursor: pointer;\n transition: all var(--dur-fast);\n }\n .icon-btn:hover {\n background: var(--prism-input-bg);\n color: var(--prism-text);\n }\n\n .panel-body {\n flex: 1;\n min-height: 0;\n overflow: auto;\n background: var(--prism-bg-elevated);\n }\n .panel-body::-webkit-scrollbar { width: 8px; height: 8px; }\n .panel-body::-webkit-scrollbar-thumb { background: var(--prism-border-strong); border-radius: 4px; }\n\n .panel-pane { height: 100%; }\n .panel-pane[hidden] { display: none; }\n\n :focus-visible {\n outline: 2px solid var(--prism-primary);\n outline-offset: 2px;\n }\n "] }]
228
275
  }], () => [], null); })();
229
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismPanelHostComponent, { className: "PrismPanelHostComponent", filePath: "app/panels/panel-host/prism-panel-host.component.ts", lineNumber: 196 }); })();
276
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismPanelHostComponent, { className: "PrismPanelHostComponent", filePath: "app/panels/panel-host/prism-panel-host.component.ts", lineNumber: 213 }); })();
@@ -1 +1 @@
1
- {"version":3,"file":"provide-prism.d.ts","sourceRoot":"","sources":["../../src/app/provide-prism.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAEpF,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAStE,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACzC;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,eAAe,EACzB,MAAM,CAAC,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,oBAAoB,CAkBtB"}
1
+ {"version":3,"file":"provide-prism.d.ts","sourceRoot":"","sources":["../../src/app/provide-prism.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAEpF,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAStE,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACzC;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,eAAe,EACzB,MAAM,CAAC,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,oBAAoB,CA8BtB"}
@@ -7,6 +7,17 @@ export function providePrism(manifest, config, options) {
7
7
  const mergedManifest = options?.componentPages?.length
8
8
  ? { ...manifest, pages: [...(manifest.pages ?? []), ...options.componentPages.map(o => componentPage(o))] }
9
9
  : manifest;
10
+ // Expose a thin discovery view of the manifest to audit tooling (e.g. ng-prism-audit-a11y).
11
+ // We intentionally only expose className + variant info — no Angular type references.
12
+ if (typeof globalThis !== 'undefined') {
13
+ globalThis['__PRISM_MANIFEST__'] = {
14
+ components: mergedManifest.components.map((c) => ({
15
+ className: c.meta.className,
16
+ variants: c.meta.showcaseConfig.variants?.map((v, i) => ({ name: v.name, index: i })) ?? [{ name: 'Default', index: 0 }],
17
+ })),
18
+ pages: (mergedManifest.pages ?? []).map((p) => ({ title: p.title })),
19
+ };
20
+ }
10
21
  return makeEnvironmentProviders([
11
22
  { provide: PRISM_MANIFEST, useValue: mergedManifest },
12
23
  { provide: PRISM_BUILTIN_PANELS, useValue: BUILTIN_PANELS },
@@ -25,6 +25,8 @@ export declare class PrismRendererComponent {
25
25
  private isRenderPage;
26
26
  private readonly overlayCache;
27
27
  readonly activeOverlay: import("@angular/core").WritableSignal<Type<unknown> | null>;
28
+ /** Identifier of the currently rendered component/variant (for audit tooling / e2e). */
29
+ protected readonly renderedKey: import("@angular/core").Signal<string | null>;
28
30
  protected readonly overlayInputs: {
29
31
  rendererService: PrismRendererService;
30
32
  };
@@ -1 +1 @@
1
- {"version":3,"file":"prism-renderer.component.d.ts","sourceRoot":"","sources":["../../../src/app/renderer/prism-renderer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,QAAQ,EAER,KAAK,IAAI,EAIV,MAAM,eAAe,CAAC;AAYvB,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AAGjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;;AAIhF,qBAyIa,sBAAsB;IACjC,SAAS,CAAC,QAAQ,CAAC,IAAI,OAAQ;IAC/B,SAAS,CAAC,QAAQ,CAAC,iBAAiB,yBAAkC;IACtE,SAAS,CAAC,QAAQ,CAAC,eAAe,uBAAgC;IAClE,SAAS,CAAC,QAAQ,CAAC,aAAa,qBAA8B;IAC9D,SAAS,CAAC,QAAQ,CAAC,SAAS,wBAAiC;IAC7D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsB;IACjD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmC;IACxD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAE3B;IAEH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA8B;IAE5D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAEpB;IACH,OAAO,CAAC,YAAY,CAAsC;IAC1D,OAAO,CAAC,mBAAmB,CAAsC;IACjE,OAAO,CAAC,oBAAoB,CAChB;IACZ,OAAO,CAAC,YAAY,CAAS;IAE7B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoC;IACjE,QAAQ,CAAC,aAAa,+DAAsC;IAC5D,SAAS,CAAC,QAAQ,CAAC,aAAa;;MAA6C;IAC7E,SAAS,CAAC,QAAQ,CAAC,eAAe,yFAG/B;;IA0FH,OAAO,CAAC,eAAe;IA6FvB,OAAO,CAAC,OAAO;yCAxNJ,sBAAsB;2CAAtB,sBAAsB;CAsOlC"}
1
+ {"version":3,"file":"prism-renderer.component.d.ts","sourceRoot":"","sources":["../../../src/app/renderer/prism-renderer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,QAAQ,EAER,KAAK,IAAI,EAIV,MAAM,eAAe,CAAC;AAYvB,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AAGjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;;AAIhF,qBA6Ia,sBAAsB;IACjC,SAAS,CAAC,QAAQ,CAAC,IAAI,OAAQ;IAC/B,SAAS,CAAC,QAAQ,CAAC,iBAAiB,yBAAkC;IACtE,SAAS,CAAC,QAAQ,CAAC,eAAe,uBAAgC;IAClE,SAAS,CAAC,QAAQ,CAAC,aAAa,qBAA8B;IAC9D,SAAS,CAAC,QAAQ,CAAC,SAAS,wBAAiC;IAC7D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsB;IACjD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmC;IACxD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAE3B;IAEH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA8B;IAE5D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAEpB;IACH,OAAO,CAAC,YAAY,CAAsC;IAC1D,OAAO,CAAC,mBAAmB,CAAsC;IACjE,OAAO,CAAC,oBAAoB,CAChB;IACZ,OAAO,CAAC,YAAY,CAAS;IAE7B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoC;IACjE,QAAQ,CAAC,aAAa,+DAAsC;IAC5D,wFAAwF;IACxF,SAAS,CAAC,QAAQ,CAAC,WAAW,gDAM3B;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa;;MAA6C;IAC7E,SAAS,CAAC,QAAQ,CAAC,eAAe,yFAG/B;;IA0FH,OAAO,CAAC,eAAe;IA6FvB,OAAO,CAAC,OAAO;yCAhOJ,sBAAsB;2CAAtB,sBAAsB;CA8OlC"}
@@ -48,6 +48,16 @@ export class PrismRendererComponent {
48
48
  isRenderPage = false;
49
49
  overlayCache = new Map();
50
50
  activeOverlay = signal(null, ...(ngDevMode ? [{ debugName: "activeOverlay" }] : []));
51
+ /** Identifier of the currently rendered component/variant (for audit tooling / e2e). */
52
+ renderedKey = computed(() => {
53
+ const el = this.rendererService.renderedElement();
54
+ if (!el)
55
+ return null;
56
+ const comp = this.navigationService.activeComponent();
57
+ if (!comp)
58
+ return null;
59
+ return `${comp.meta.className}:${this.rendererService.activeVariantIndex()}`;
60
+ }, ...(ngDevMode ? [{ debugName: "renderedKey" }] : []));
51
61
  overlayInputs = { rendererService: this.rendererService };
52
62
  overlayInjector = computed(() => {
53
63
  const panelInjector = this.panelService.activePanelInjector();
@@ -207,7 +217,7 @@ export class PrismRendererComponent {
207
217
  i0.ɵɵviewQuerySignal(ctx.outlet, _c0, 5, ViewContainerRef);
208
218
  } if (rf & 2) {
209
219
  i0.ɵɵqueryAdvance();
210
- } }, decls: 11, vars: 8, consts: [["outlet", ""], [1, "prism-canvas-stage"], [1, "canvas-badges"], [1, "c-badge"], [1, "stage-crosshair"], [1, "demo-wrap"], [4, "ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector"]], template: function PrismRendererComponent_Template(rf, ctx) { if (rf & 1) {
220
+ } }, decls: 11, vars: 9, consts: [["outlet", ""], [1, "prism-canvas-stage"], [1, "canvas-badges"], [1, "c-badge"], [1, "stage-crosshair"], [1, "demo-wrap"], [4, "ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector"]], template: function PrismRendererComponent_Template(rf, ctx) { if (rf & 1) {
211
221
  i0.ɵɵelementStart(0, "div", 1)(1, "div", 2)(2, "span", 3);
212
222
  i0.ɵɵtext(3);
213
223
  i0.ɵɵelementEnd()();
@@ -224,6 +234,7 @@ export class PrismRendererComponent {
224
234
  i0.ɵɵclassProp("visible", ctx.canvasService.guides());
225
235
  i0.ɵɵadvance(3);
226
236
  i0.ɵɵstyleProp("--zoom", ctx.canvasService.zoom());
237
+ i0.ɵɵattribute("data-prism-rendered", ctx.renderedKey());
227
238
  i0.ɵɵadvance(3);
228
239
  i0.ɵɵconditional(ctx.activeOverlay() ? 10 : -1);
229
240
  } }, dependencies: [PrismCanvasRulersComponent,
@@ -254,7 +265,11 @@ export class PrismRendererComponent {
254
265
  <prism-canvas-rulers />
255
266
  <prism-canvas-bg-pill />
256
267
 
257
- <div class="demo-wrap" [style.--zoom]="canvasService.zoom()">
268
+ <div
269
+ class="demo-wrap"
270
+ [style.--zoom]="canvasService.zoom()"
271
+ [attr.data-prism-rendered]="renderedKey()"
272
+ >
258
273
  <ng-container #outlet />
259
274
  @if (activeOverlay()) {
260
275
  <ng-container
@@ -271,7 +286,7 @@ export class PrismRendererComponent {
271
286
  }], () => [], { outlet: [{ type: i0.ViewChild, args: ['outlet', { ...{
272
287
  read: ViewContainerRef,
273
288
  }, isSignal: true }] }] }); })();
274
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismRendererComponent, { className: "PrismRendererComponent", filePath: "app/renderer/prism-renderer.component.ts", lineNumber: 174 }); })();
289
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismRendererComponent, { className: "PrismRendererComponent", filePath: "app/renderer/prism-renderer.component.ts", lineNumber: 178 }); })();
275
290
  function parseContentToNodes(content) {
276
291
  if (typeof content === 'string') {
277
292
  return [htmlToNodes(content)];
@@ -8,6 +8,11 @@ export declare class PrismPanelService {
8
8
  readonly activePanelId: import("@angular/core").WritableSignal<string>;
9
9
  readonly activeViewId: import("@angular/core").WritableSignal<string>;
10
10
  readonly activePanelInjector: import("@angular/core").Signal<EnvironmentInjector | null>;
11
+ /**
12
+ * Returns (and lazily creates) the EnvironmentInjector scoped to the given panel.
13
+ * Returns `null` when the panel has no providers.
14
+ */
15
+ getInjector(panelId: string): EnvironmentInjector | null;
11
16
  static ɵfac: i0.ɵɵFactoryDeclaration<PrismPanelService, never>;
12
17
  static ɵprov: i0.ɵɵInjectableDeclaration<PrismPanelService>;
13
18
  }
@@ -1 +1 @@
1
- {"version":3,"file":"prism-panel.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-panel.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,mBAAmB,EAIpB,MAAM,eAAe,CAAC;;AAIvB,qBACa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA8B;IAC5D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgC;IAC9D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0C;IAExE,QAAQ,CAAC,aAAa,iDAA8B;IACpD,QAAQ,CAAC,YAAY,iDAA8B;IAEnD,QAAQ,CAAC,mBAAmB,6DAgBzB;yCAzBQ,iBAAiB;6CAAjB,iBAAiB;CA0B7B"}
1
+ {"version":3,"file":"prism-panel.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-panel.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,mBAAmB,EAIpB,MAAM,eAAe,CAAC;;AAIvB,qBACa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAA+B;IAC3D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA8B;IAC5D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgC;IAC9D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA0C;IAExE,QAAQ,CAAC,aAAa,iDAA8B;IACpD,QAAQ,CAAC,YAAY,iDAA8B;IAEnD,QAAQ,CAAC,mBAAmB,6DAE1B;IAEF;;;OAGG;IACH,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI;yCAjB7C,iBAAiB;6CAAjB,iBAAiB;CAiC7B"}
@@ -9,8 +9,12 @@ export class PrismPanelService {
9
9
  injectorCache = new Map();
10
10
  activePanelId = signal('controls', ...(ngDevMode ? [{ debugName: "activePanelId" }] : []));
11
11
  activeViewId = signal('renderer', ...(ngDevMode ? [{ debugName: "activeViewId" }] : []));
12
- activePanelInjector = computed(() => {
13
- const panelId = this.activePanelId();
12
+ activePanelInjector = computed(() => this.getInjector(this.activePanelId()), ...(ngDevMode ? [{ debugName: "activePanelInjector" }] : []));
13
+ /**
14
+ * Returns (and lazily creates) the EnvironmentInjector scoped to the given panel.
15
+ * Returns `null` when the panel has no providers.
16
+ */
17
+ getInjector(panelId) {
14
18
  const allPanels = [...this.builtinPanels, ...this.pluginService.panels()];
15
19
  const panel = allPanels.find((p) => p.id === panelId);
16
20
  if (!panel?.providers?.length)
@@ -21,7 +25,7 @@ export class PrismPanelService {
21
25
  this.injectorCache.set(panel.id, injector);
22
26
  }
23
27
  return injector;
24
- }, ...(ngDevMode ? [{ debugName: "activePanelInjector" }] : []));
28
+ }
25
29
  static ɵfac = function PrismPanelService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PrismPanelService)(); };
26
30
  static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: PrismPanelService, factory: PrismPanelService.ɵfac, providedIn: 'root' });
27
31
  }
@@ -1,4 +1,4 @@
1
- import type { ControlDefinition, PanelDefinition } from '../../plugin/plugin.types.js';
1
+ import type { ControlDefinition, HeaderWidgetDefinition, PanelDefinition } from '../../plugin/plugin.types.js';
2
2
  import * as i0 from "@angular/core";
3
3
  export declare class PrismPluginService {
4
4
  private readonly config;
@@ -6,6 +6,9 @@ export declare class PrismPluginService {
6
6
  readonly addonPanels: import("@angular/core").Signal<PanelDefinition[]>;
7
7
  readonly viewPanels: import("@angular/core").Signal<PanelDefinition[]>;
8
8
  readonly controls: import("@angular/core").Signal<ControlDefinition[]>;
9
+ readonly headerWidgets: import("@angular/core").Signal<HeaderWidgetDefinition[]>;
10
+ readonly headerWidgetsStart: import("@angular/core").Signal<HeaderWidgetDefinition[]>;
11
+ readonly headerWidgetsEnd: import("@angular/core").Signal<HeaderWidgetDefinition[]>;
9
12
  static ɵfac: i0.ɵɵFactoryDeclaration<PrismPluginService, never>;
10
13
  static ɵprov: i0.ɵɵInjectableDeclaration<PrismPluginService>;
11
14
  }
@@ -1 +1 @@
1
- {"version":3,"file":"prism-plugin.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-plugin.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;;AAGvF,qBACa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAE/C,QAAQ,CAAC,MAAM,oDAQZ;IAEH,QAAQ,CAAC,WAAW,oDAElB;IAEF,QAAQ,CAAC,UAAU,oDAEjB;IAEF,QAAQ,CAAC,QAAQ,sDAQd;yCA7BQ,kBAAkB;6CAAlB,kBAAkB;CA8B9B"}
1
+ {"version":3,"file":"prism-plugin.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-plugin.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,iBAAiB,EACjB,sBAAsB,EACtB,eAAe,EAChB,MAAM,8BAA8B,CAAC;;AAGtC,qBACa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAE/C,QAAQ,CAAC,MAAM,oDAQZ;IAEH,QAAQ,CAAC,WAAW,oDAElB;IAEF,QAAQ,CAAC,UAAU,oDAEjB;IAEF,QAAQ,CAAC,QAAQ,sDAQd;IAEH,QAAQ,CAAC,aAAa,2DAQnB;IAEH,QAAQ,CAAC,kBAAkB,2DAEzB;IAEF,QAAQ,CAAC,gBAAgB,2DAEvB;yCA/CS,kBAAkB;6CAAlB,kBAAkB;CAgD9B"}
@@ -23,6 +23,17 @@ export class PrismPluginService {
23
23
  }
24
24
  return result;
25
25
  }, ...(ngDevMode ? [{ debugName: "controls" }] : []));
26
+ headerWidgets = computed(() => {
27
+ const result = [];
28
+ for (const plugin of this.config.plugins ?? []) {
29
+ if (plugin.headerWidgets) {
30
+ result.push(...plugin.headerWidgets);
31
+ }
32
+ }
33
+ return result.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
34
+ }, ...(ngDevMode ? [{ debugName: "headerWidgets" }] : []));
35
+ headerWidgetsStart = computed(() => this.headerWidgets().filter((w) => w.placement === 'start'), ...(ngDevMode ? [{ debugName: "headerWidgetsStart" }] : []));
36
+ headerWidgetsEnd = computed(() => this.headerWidgets().filter((w) => (w.placement ?? 'end') === 'end'), ...(ngDevMode ? [{ debugName: "headerWidgetsEnd" }] : []));
26
37
  static ɵfac = function PrismPluginService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PrismPluginService)(); };
27
38
  static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: PrismPluginService, factory: PrismPluginService.ɵfac, providedIn: 'root' });
28
39
  }
@@ -0,0 +1,12 @@
1
+ import type { A11yManifestMeta, A11yReport, A11yThresholds } from '../../app/panels/a11y/a11y.types.js';
2
+ export declare const DEFAULT_A11Y_REPORT_PATH = "a11y-report.json";
3
+ export declare function loadA11yReport(reportPath: string): A11yReport | null;
4
+ export declare function readA11yMeta(reportPath: string, thresholdsInput?: Partial<A11yThresholds>): A11yManifestMeta | null;
5
+ export interface ThresholdViolation {
6
+ metric: 'score' | 'critical' | 'serious' | 'moderate';
7
+ actual: number;
8
+ threshold: number;
9
+ }
10
+ export declare function checkA11yThresholds(meta: A11yManifestMeta): ThresholdViolation[];
11
+ export declare function clearA11yReportCache(): void;
12
+ //# sourceMappingURL=a11y-report-reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"a11y-report-reader.d.ts","sourceRoot":"","sources":["../../../src/builder/a11y/a11y-report-reader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EACV,cAAc,EACf,MAAM,qCAAqC,CAAC;AAG7C,eAAO,MAAM,wBAAwB,qBAAqB,CAAC;AAI3D,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAapE;AAED,wBAAgB,YAAY,CAC1B,UAAU,EAAE,MAAM,EAClB,eAAe,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,GACxC,gBAAgB,GAAG,IAAI,CAQzB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,CAAC;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,gBAAgB,GACrB,kBAAkB,EAAE,CAkBtB;AAED,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C"}
@@ -0,0 +1,49 @@
1
+ import { readFileSync, statSync } from 'node:fs';
2
+ import { resolveA11yThresholds } from '../../app/panels/a11y/a11y-thresholds.js';
3
+ export const DEFAULT_A11Y_REPORT_PATH = 'a11y-report.json';
4
+ const cache = new Map();
5
+ export function loadA11yReport(reportPath) {
6
+ try {
7
+ const mtime = statSync(reportPath).mtimeMs;
8
+ const cached = cache.get(reportPath);
9
+ if (cached && cached.mtime === mtime)
10
+ return cached.data;
11
+ const raw = readFileSync(reportPath, 'utf-8');
12
+ const parsed = JSON.parse(raw);
13
+ cache.set(reportPath, { mtime, data: parsed });
14
+ return parsed;
15
+ }
16
+ catch {
17
+ return null;
18
+ }
19
+ }
20
+ export function readA11yMeta(reportPath, thresholdsInput) {
21
+ const report = loadA11yReport(reportPath);
22
+ if (!report?.total)
23
+ return null;
24
+ return {
25
+ total: report.total,
26
+ thresholds: resolveA11yThresholds(thresholdsInput),
27
+ };
28
+ }
29
+ export function checkA11yThresholds(meta) {
30
+ const violations = [];
31
+ const t = meta.thresholds;
32
+ const total = meta.total;
33
+ if (total.score < t.score) {
34
+ violations.push({ metric: 'score', actual: total.score, threshold: t.score });
35
+ }
36
+ if (total.critical > t.critical) {
37
+ violations.push({ metric: 'critical', actual: total.critical, threshold: t.critical });
38
+ }
39
+ if (total.serious > t.serious) {
40
+ violations.push({ metric: 'serious', actual: total.serious, threshold: t.serious });
41
+ }
42
+ if (total.moderate > t.moderate) {
43
+ violations.push({ metric: 'moderate', actual: total.moderate, threshold: t.moderate });
44
+ }
45
+ return violations;
46
+ }
47
+ export function clearA11yReportCache() {
48
+ cache.clear();
49
+ }
@@ -4,6 +4,7 @@ export interface RuntimeManifestOptions {
4
4
  components: ScannedComponent[];
5
5
  libraryImportPath: string;
6
6
  pages?: StyleguidePage[];
7
+ meta?: Record<string, unknown>;
7
8
  }
8
9
  export declare function generateRuntimeManifest(options: RuntimeManifestOptions): string;
9
10
  //# sourceMappingURL=runtime-manifest.generator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-manifest.generator.d.ts","sourceRoot":"","sources":["../../../src/builder/manifest/runtime-manifest.generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAa,MAAM,8BAA8B,CAAC;AAChF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAIjE,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;CAC1B;AAyHD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CAsE/E"}
1
+ {"version":3,"file":"runtime-manifest.generator.d.ts","sourceRoot":"","sources":["../../../src/builder/manifest/runtime-manifest.generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAa,MAAM,8BAA8B,CAAC;AAChF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAIjE,MAAM,WAAW,sBAAsB;IACrC,UAAU,EAAE,gBAAgB,EAAE,CAAC;IAC/B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAyHD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CA8E/E"}
@@ -108,7 +108,7 @@ function groupComponentsByImportPath(components, fallbackImportPath) {
108
108
  return groups;
109
109
  }
110
110
  export function generateRuntimeManifest(options) {
111
- const { components, libraryImportPath, pages } = options;
111
+ const { components, libraryImportPath, pages, meta } = options;
112
112
  const directives = components.filter((c) => c.componentMeta.isDirective);
113
113
  const hasDirectives = directives.length > 0;
114
114
  const lines = [
@@ -162,6 +162,13 @@ export function generateRuntimeManifest(options) {
162
162
  if (pages && pages.length > 0) {
163
163
  lines.push(` pages: ${JSON.stringify(pages, null, 2).split('\n').map((l, i) => i === 0 ? l : ' ' + l).join('\n')},`);
164
164
  }
165
+ if (meta && Object.keys(meta).length > 0) {
166
+ const metaJson = JSON.stringify(meta, null, 2)
167
+ .split('\n')
168
+ .map((l, i) => (i === 0 ? l : ' ' + l))
169
+ .join('\n');
170
+ lines.push(` meta: ${metaJson},`);
171
+ }
165
172
  lines.push('};');
166
173
  lines.push('');
167
174
  return lines.join('\n');
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-runner.d.ts","sourceRoot":"","sources":["../../../src/builder/plugin-runner/plugin-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAoB,MAAM,8BAA8B,CAAC;AAmBnG,wBAAsB,cAAc,CAClC,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,aAAa,EAAE,GACvB,OAAO,CAAC,aAAa,CAAC,CAuDxB"}
1
+ {"version":3,"file":"plugin-runner.d.ts","sourceRoot":"","sources":["../../../src/builder/plugin-runner/plugin-runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAoB,MAAM,8BAA8B,CAAC;AAmBnG,wBAAsB,cAAc,CAClC,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,aAAa,EAAE,GACvB,OAAO,CAAC,aAAa,CAAC,CAyDxB"}
@@ -46,7 +46,9 @@ export async function runPluginHooks(manifest, plugins) {
46
46
  }
47
47
  pages[i] = current;
48
48
  }
49
- let result = { components, pages };
49
+ let result = manifest.meta
50
+ ? { components, pages, meta: manifest.meta }
51
+ : { components, pages };
50
52
  for (const plugin of plugins) {
51
53
  if (plugin.onManifestReady) {
52
54
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"prism-pipeline.d.ts","sourceRoot":"","sources":["../../../src/builder/shared/prism-pipeline.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAOpE,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,mGAAmG;IACnG,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,oGAAoG;IACpG,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,wBAAgB,mBAAmB,IAAI,kBAAkB,CAExD;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CAoD9B"}
1
+ {"version":3,"file":"prism-pipeline.d.ts","sourceRoot":"","sources":["../../../src/builder/shared/prism-pipeline.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAGhE,OAAO,EAAiB,KAAK,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAYpE,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,mGAAmG;IACnG,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,oGAAoG;IACpG,eAAe,EAAE,MAAM,GAAG,SAAS,CAAC;CACrC;AAED,wBAAgB,mBAAmB,IAAI,kBAAkB,CAExD;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,cAAc,EACvB,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,mBAAmB,CAAC,CA2E9B"}
@@ -6,6 +6,7 @@ import { discoverSecondaryEntryPoints } from '../scanner/entry-point-discovery.j
6
6
  import { loadConfig } from '../config-loader/config-loader.js';
7
7
  import { runPluginHooks } from '../plugin-runner/plugin-runner.js';
8
8
  import { generateRuntimeManifest } from '../manifest/runtime-manifest.generator.js';
9
+ import { DEFAULT_A11Y_REPORT_PATH, checkA11yThresholds, readA11yMeta, } from '../a11y/a11y-report-reader.js';
9
10
  export function createPipelineState() {
10
11
  return { scanner: undefined, lastEntrySetKey: undefined };
11
12
  }
@@ -20,12 +21,29 @@ export async function runPrismPipeline(options, context, state) {
20
21
  const scanResult = scanEntryPoints(workspaceRoot, options, state);
21
22
  const pages = config.pages ? [...config.pages] : [];
22
23
  context.reportStatus('Running plugin hooks...');
23
- const manifest = await runPluginHooks({ ...scanResult, pages }, config.plugins ?? []);
24
+ let manifest = await runPluginHooks({ ...scanResult, pages }, config.plugins ?? []);
25
+ const a11yReportPath = config.a11y?.reportPath ?? DEFAULT_A11Y_REPORT_PATH;
26
+ const a11yMeta = readA11yMeta(join(workspaceRoot, a11yReportPath), config.a11y?.thresholds);
27
+ if (a11yMeta) {
28
+ const violations = checkA11yThresholds(a11yMeta);
29
+ if (violations.length > 0) {
30
+ const summary = violations
31
+ .map((v) => `${v.metric}: ${v.actual} (threshold ${v.threshold})`)
32
+ .join(', ');
33
+ throw new Error(`ng-prism: a11y thresholds violated — ${summary}. ` +
34
+ `Update components or relax thresholds via config.a11y.thresholds.`);
35
+ }
36
+ manifest = {
37
+ ...manifest,
38
+ meta: { ...manifest.meta, a11y: a11yMeta },
39
+ };
40
+ }
24
41
  context.reportStatus('Generating runtime manifest...');
25
42
  const source = generateRuntimeManifest({
26
43
  components: manifest.components,
27
44
  libraryImportPath: options.libraryImportPath,
28
45
  pages: manifest.pages,
46
+ meta: manifest.meta,
29
47
  });
30
48
  const projectMeta = await context.getProjectMetadata(options.prismProject);
31
49
  const sourceRoot = projectMeta['sourceRoot'] ??
@@ -0,0 +1,10 @@
1
+ import type { A11yReport, A11yScoreResult } from '../../app/panels/a11y/a11y.types.js';
2
+ export interface RawAuditResult {
3
+ className: string;
4
+ variantName: string;
5
+ variantIndex: number;
6
+ /** Result from `calculateScore(axeResults)` in the running app. */
7
+ score: A11yScoreResult;
8
+ }
9
+ export declare function aggregateAuditResults(results: RawAuditResult[]): A11yReport;
10
+ //# sourceMappingURL=aggregator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aggregator.d.ts","sourceRoot":"","sources":["../../../src/cli/audit-a11y/aggregator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,eAAe,EAChB,MAAM,qCAAqC,CAAC;AAE7C,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,mEAAmE;IACnE,KAAK,EAAE,eAAe,CAAC;CACxB;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,EAAE,GAAG,UAAU,CAkD3E"}