@ng-prism/core 21.7.2 → 21.8.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 (59) hide show
  1. package/README.md +4 -4
  2. package/dist/app/index.d.ts +2 -2
  3. package/dist/app/index.d.ts.map +1 -1
  4. package/dist/app/index.js +4 -1
  5. package/dist/app/panels/a11y/a11y-audit.service.d.ts +2 -0
  6. package/dist/app/panels/a11y/a11y-audit.service.d.ts.map +1 -1
  7. package/dist/app/panels/a11y/a11y-audit.service.js +15 -2
  8. package/dist/app/panels/a11y/a11y-keyboard.component.d.ts +1 -2
  9. package/dist/app/panels/a11y/a11y-keyboard.component.d.ts.map +1 -1
  10. package/dist/app/panels/a11y/a11y-keyboard.component.js +4 -5
  11. package/dist/app/panels/a11y/a11y-panel.component.d.ts +2 -1
  12. package/dist/app/panels/a11y/a11y-panel.component.d.ts.map +1 -1
  13. package/dist/app/panels/a11y/a11y-panel.component.js +10 -19
  14. package/dist/app/panels/a11y/a11y-sr.component.d.ts +1 -2
  15. package/dist/app/panels/a11y/a11y-sr.component.d.ts.map +1 -1
  16. package/dist/app/panels/a11y/a11y-sr.component.js +4 -5
  17. package/dist/app/panels/a11y/a11y-tree.component.d.ts +3 -2
  18. package/dist/app/panels/a11y/a11y-tree.component.d.ts.map +1 -1
  19. package/dist/app/panels/a11y/a11y-tree.component.js +21 -9
  20. package/dist/app/panels/controls/prism-controls-panel.component.d.ts +3 -2
  21. package/dist/app/panels/controls/prism-controls-panel.component.d.ts.map +1 -1
  22. package/dist/app/panels/controls/prism-controls-panel.component.js +5 -4
  23. package/dist/app/panels/events/prism-events-panel.component.d.ts +3 -1
  24. package/dist/app/panels/events/prism-events-panel.component.d.ts.map +1 -1
  25. package/dist/app/panels/events/prism-events-panel.component.js +5 -4
  26. package/dist/app/renderer/prism-renderer.component.d.ts.map +1 -1
  27. package/dist/app/renderer/prism-renderer.component.js +8 -5
  28. package/dist/app/renderer/snippet-generator.d.ts.map +1 -1
  29. package/dist/app/renderer/snippet-generator.js +6 -3
  30. package/dist/app/services/prism-persistence.service.d.ts +20 -0
  31. package/dist/app/services/prism-persistence.service.d.ts.map +1 -0
  32. package/dist/app/services/prism-persistence.service.js +139 -0
  33. package/dist/app/services/prism-renderer.service.d.ts.map +1 -1
  34. package/dist/app/services/prism-renderer.service.js +8 -2
  35. package/dist/app/services/prism-url-state.service.d.ts.map +1 -1
  36. package/dist/app/services/prism-url-state.service.js +12 -2
  37. package/dist/app/shell/prism-shell.component.d.ts +1 -0
  38. package/dist/app/shell/prism-shell.component.d.ts.map +1 -1
  39. package/dist/app/shell/prism-shell.component.js +4 -1
  40. package/dist/builder/config-loader/config-loader.d.ts.map +1 -1
  41. package/dist/builder/config-loader/config-loader.js +9 -3
  42. package/dist/builder/plugin-runner/plugin-runner.d.ts.map +1 -1
  43. package/dist/builder/plugin-runner/plugin-runner.js +40 -14
  44. package/dist/builder/scanner/component.scanner.js +9 -4
  45. package/dist/builder/scanner/entry-point-discovery.js +24 -15
  46. package/dist/builder/serve/index.d.ts.map +1 -1
  47. package/dist/builder/serve/index.js +8 -4
  48. package/dist/builder/shared/prism-pipeline.js +11 -3
  49. package/dist/builder/watcher/prism-watcher.d.ts.map +1 -1
  50. package/dist/builder/watcher/prism-watcher.js +31 -20
  51. package/dist/config/index.d.ts +1 -1
  52. package/dist/config/index.d.ts.map +1 -1
  53. package/dist/plugin/define-config.d.ts.map +1 -1
  54. package/dist/plugin/define-config.js +20 -0
  55. package/dist/plugin/plugin.types.d.ts +2 -0
  56. package/dist/plugin/plugin.types.d.ts.map +1 -1
  57. package/dist/schematics/ng-add/index.d.ts.map +1 -1
  58. package/dist/schematics/ng-add/index.js +91 -59
  59. package/package.json +1 -1
@@ -113,9 +113,12 @@ export class PrismRendererComponent {
113
113
  return;
114
114
  }
115
115
  this.activeOverlay.set(null);
116
+ const requestedPanelId = panel.id;
116
117
  panel.loadOverlayComponent().then((c) => {
117
- this.overlayCache.set(panel.id, c);
118
- this.activeOverlay.set(c);
118
+ this.overlayCache.set(requestedPanelId, c);
119
+ if (this.panelService.activePanelId() === requestedPanelId) {
120
+ this.activeOverlay.set(c);
121
+ }
119
122
  });
120
123
  return;
121
124
  }
@@ -220,7 +223,7 @@ export class PrismRendererComponent {
220
223
  i0.ɵɵstyleProp("--zoom", ctx.canvasService.zoom());
221
224
  i0.ɵɵadvance(3);
222
225
  i0.ɵɵconditional(ctx.activeOverlay() ? 9 : -1);
223
- } }, dependencies: [PrismCanvasRulersComponent, NgComponentOutlet], styles: ["[_nghost-%COMP%] { display: block; min-height: 0; flex: 1; }\n\n .prism-canvas-stage[_ngcontent-%COMP%] {\n position: relative;\n overflow: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 32px;\n min-height: 200px;\n height: 100%;\n background-color: var(--prism-bg-surface);\n background-image: radial-gradient(circle, var(--prism-dot) 1px, transparent 1px);\n background-size: 20px 20px;\n transition: filter var(--dur-base);\n }\n\n .prism-canvas-stage[data-bg=\"plain\"][_ngcontent-%COMP%] {\n background-image: none;\n }\n .prism-canvas-stage[data-bg=\"light\"][_ngcontent-%COMP%] {\n background-color: var(--prism-void-light, #f7f5fc);\n background-image: radial-gradient(circle, color-mix(in srgb, var(--prism-primary-from) 15%, transparent) 1px, transparent 1px);\n }\n .prism-canvas-stage[data-bg=\"dark\"][_ngcontent-%COMP%] {\n background-color: var(--prism-void-dark, #07050f);\n background-image: radial-gradient(circle, color-mix(in srgb, var(--prism-primary) 18%, transparent) 1px, transparent 1px);\n }\n .prism-canvas-stage[data-bg=\"checker\"][_ngcontent-%COMP%] {\n background-image:\n linear-gradient(45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(-45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, var(--prism-border) 75%),\n linear-gradient(-45deg, transparent 75%, var(--prism-border) 75%);\n background-size: 16px 16px;\n background-position: 0 0, 0 8px, 8px -8px, -8px 0;\n }\n\n .stage-crosshair[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n pointer-events: none;\n opacity: 0;\n transition: opacity var(--dur-base);\n }\n .stage-crosshair.visible[_ngcontent-%COMP%] { opacity: 1; }\n .stage-crosshair[_ngcontent-%COMP%]::before, \n .stage-crosshair[_ngcontent-%COMP%]::after {\n content: '';\n position: absolute;\n background: color-mix(in srgb, var(--prism-primary) 20%, transparent);\n }\n .stage-crosshair[_ngcontent-%COMP%]::before { left: 0; right: 0; top: 50%; height: 1px; }\n .stage-crosshair[_ngcontent-%COMP%]::after { top: 0; bottom: 0; left: 50%; width: 1px; }\n\n .canvas-badges[_ngcontent-%COMP%] {\n position: absolute;\n top: 12px;\n left: 20px;\n display: flex;\n gap: 6px;\n pointer-events: none;\n }\n .c-badge[_ngcontent-%COMP%] {\n font-family: var(--font-mono);\n font-size: var(--fs-xs);\n padding: 3px 7px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--prism-bg-elevated) 90%, transparent);\n border: 1px solid var(--prism-border);\n color: var(--prism-text-muted);\n backdrop-filter: blur(8px);\n }\n\n .demo-wrap[_ngcontent-%COMP%] {\n position: relative;\n transform: scale(var(--zoom, 1));\n transition: transform 0.18s;\n }"], changeDetection: 0 });
226
+ } }, dependencies: [PrismCanvasRulersComponent, NgComponentOutlet], styles: ["[_nghost-%COMP%] { display: block; min-height: 0; flex: 1; }\n\n .prism-canvas-stage[_ngcontent-%COMP%] {\n position: relative;\n overflow: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 32px;\n min-height: 200px;\n height: 100%;\n background-color: var(--prism-bg-surface);\n background-image: radial-gradient(circle, var(--prism-dot) 1px, transparent 1px);\n background-size: 20px 20px;\n transition: filter var(--dur-base);\n }\n\n .prism-canvas-stage[data-bg=\"plain\"][_ngcontent-%COMP%] {\n background-image: none;\n }\n .prism-canvas-stage[data-bg=\"light\"][_ngcontent-%COMP%] {\n background-color: var(--prism-void-light, #f7f5fc);\n background-image: radial-gradient(circle, color-mix(in srgb, var(--prism-primary-from) 15%, transparent) 1px, transparent 1px);\n }\n .prism-canvas-stage[data-bg=\"dark\"][_ngcontent-%COMP%] {\n background-color: var(--prism-void-dark, #07050f);\n background-image: radial-gradient(circle, color-mix(in srgb, var(--prism-primary) 18%, transparent) 1px, transparent 1px);\n }\n .prism-canvas-stage[data-bg=\"checker\"][_ngcontent-%COMP%] {\n background-image:\n linear-gradient(45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(-45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, var(--prism-border) 75%),\n linear-gradient(-45deg, transparent 75%, var(--prism-border) 75%);\n background-size: 16px 16px;\n background-position: 0 0, 0 8px, 8px -8px, -8px 0;\n }\n\n .stage-crosshair[_ngcontent-%COMP%] {\n position: absolute;\n inset: 0;\n pointer-events: none;\n opacity: 0;\n transition: opacity var(--dur-base);\n }\n .stage-crosshair.visible[_ngcontent-%COMP%] { opacity: 1; }\n .stage-crosshair[_ngcontent-%COMP%]::before, \n .stage-crosshair[_ngcontent-%COMP%]::after {\n content: '';\n position: absolute;\n background: color-mix(in srgb, var(--prism-primary) 20%, transparent);\n }\n .stage-crosshair[_ngcontent-%COMP%]::before { left: 0; right: 0; top: 50%; height: 1px; }\n .stage-crosshair[_ngcontent-%COMP%]::after { top: 0; bottom: 0; left: 50%; width: 1px; }\n\n .canvas-badges[_ngcontent-%COMP%] {\n position: absolute;\n top: 12px;\n left: 20px;\n display: flex;\n gap: 6px;\n pointer-events: none;\n }\n .c-badge[_ngcontent-%COMP%] {\n font-family: var(--font-mono);\n font-size: var(--fs-xs);\n padding: 3px 7px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--prism-bg-elevated) 90%, transparent);\n border: 1px solid var(--prism-border);\n color: var(--prism-text-muted);\n backdrop-filter: blur(8px);\n }\n\n .demo-wrap[_ngcontent-%COMP%] {\n position: relative;\n transform: scale(var(--zoom, 1));\n transition: transform 0.18s;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n }"], changeDetection: 0 });
224
227
  }
225
228
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PrismRendererComponent, [{
226
229
  type: Component,
@@ -250,11 +253,11 @@ export class PrismRendererComponent {
250
253
  }
251
254
  </div>
252
255
  </div>
253
- `, styles: ["\n :host { display: block; min-height: 0; flex: 1; }\n\n .prism-canvas-stage {\n position: relative;\n overflow: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 32px;\n min-height: 200px;\n height: 100%;\n background-color: var(--prism-bg-surface);\n background-image: radial-gradient(circle, var(--prism-dot) 1px, transparent 1px);\n background-size: 20px 20px;\n transition: filter var(--dur-base);\n }\n\n .prism-canvas-stage[data-bg=\"plain\"] {\n background-image: none;\n }\n .prism-canvas-stage[data-bg=\"light\"] {\n background-color: var(--prism-void-light, #f7f5fc);\n background-image: radial-gradient(circle, color-mix(in srgb, var(--prism-primary-from) 15%, transparent) 1px, transparent 1px);\n }\n .prism-canvas-stage[data-bg=\"dark\"] {\n background-color: var(--prism-void-dark, #07050f);\n background-image: radial-gradient(circle, color-mix(in srgb, var(--prism-primary) 18%, transparent) 1px, transparent 1px);\n }\n .prism-canvas-stage[data-bg=\"checker\"] {\n background-image:\n linear-gradient(45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(-45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, var(--prism-border) 75%),\n linear-gradient(-45deg, transparent 75%, var(--prism-border) 75%);\n background-size: 16px 16px;\n background-position: 0 0, 0 8px, 8px -8px, -8px 0;\n }\n\n .stage-crosshair {\n position: absolute;\n inset: 0;\n pointer-events: none;\n opacity: 0;\n transition: opacity var(--dur-base);\n }\n .stage-crosshair.visible { opacity: 1; }\n .stage-crosshair::before,\n .stage-crosshair::after {\n content: '';\n position: absolute;\n background: color-mix(in srgb, var(--prism-primary) 20%, transparent);\n }\n .stage-crosshair::before { left: 0; right: 0; top: 50%; height: 1px; }\n .stage-crosshair::after { top: 0; bottom: 0; left: 50%; width: 1px; }\n\n .canvas-badges {\n position: absolute;\n top: 12px;\n left: 20px;\n display: flex;\n gap: 6px;\n pointer-events: none;\n }\n .c-badge {\n font-family: var(--font-mono);\n font-size: var(--fs-xs);\n padding: 3px 7px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--prism-bg-elevated) 90%, transparent);\n border: 1px solid var(--prism-border);\n color: var(--prism-text-muted);\n backdrop-filter: blur(8px);\n }\n\n .demo-wrap {\n position: relative;\n transform: scale(var(--zoom, 1));\n transition: transform 0.18s;\n }\n\n "] }]
256
+ `, styles: ["\n :host { display: block; min-height: 0; flex: 1; }\n\n .prism-canvas-stage {\n position: relative;\n overflow: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 32px;\n min-height: 200px;\n height: 100%;\n background-color: var(--prism-bg-surface);\n background-image: radial-gradient(circle, var(--prism-dot) 1px, transparent 1px);\n background-size: 20px 20px;\n transition: filter var(--dur-base);\n }\n\n .prism-canvas-stage[data-bg=\"plain\"] {\n background-image: none;\n }\n .prism-canvas-stage[data-bg=\"light\"] {\n background-color: var(--prism-void-light, #f7f5fc);\n background-image: radial-gradient(circle, color-mix(in srgb, var(--prism-primary-from) 15%, transparent) 1px, transparent 1px);\n }\n .prism-canvas-stage[data-bg=\"dark\"] {\n background-color: var(--prism-void-dark, #07050f);\n background-image: radial-gradient(circle, color-mix(in srgb, var(--prism-primary) 18%, transparent) 1px, transparent 1px);\n }\n .prism-canvas-stage[data-bg=\"checker\"] {\n background-image:\n linear-gradient(45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(-45deg, var(--prism-border) 25%, transparent 25%),\n linear-gradient(45deg, transparent 75%, var(--prism-border) 75%),\n linear-gradient(-45deg, transparent 75%, var(--prism-border) 75%);\n background-size: 16px 16px;\n background-position: 0 0, 0 8px, 8px -8px, -8px 0;\n }\n\n .stage-crosshair {\n position: absolute;\n inset: 0;\n pointer-events: none;\n opacity: 0;\n transition: opacity var(--dur-base);\n }\n .stage-crosshair.visible { opacity: 1; }\n .stage-crosshair::before,\n .stage-crosshair::after {\n content: '';\n position: absolute;\n background: color-mix(in srgb, var(--prism-primary) 20%, transparent);\n }\n .stage-crosshair::before { left: 0; right: 0; top: 50%; height: 1px; }\n .stage-crosshair::after { top: 0; bottom: 0; left: 50%; width: 1px; }\n\n .canvas-badges {\n position: absolute;\n top: 12px;\n left: 20px;\n display: flex;\n gap: 6px;\n pointer-events: none;\n }\n .c-badge {\n font-family: var(--font-mono);\n font-size: var(--fs-xs);\n padding: 3px 7px;\n border-radius: 4px;\n background: color-mix(in srgb, var(--prism-bg-elevated) 90%, transparent);\n border: 1px solid var(--prism-border);\n color: var(--prism-text-muted);\n backdrop-filter: blur(8px);\n }\n\n .demo-wrap {\n position: relative;\n transform: scale(var(--zoom, 1));\n transition: transform 0.18s;\n display: flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n }\n\n "] }]
254
257
  }], () => [], { outlet: [{ type: i0.ViewChild, args: ['outlet', { ...{
255
258
  read: ViewContainerRef,
256
259
  }, isSignal: true }] }] }); })();
257
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismRendererComponent, { className: "PrismRendererComponent", filePath: "app/renderer/prism-renderer.component.ts", lineNumber: 150 }); })();
260
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismRendererComponent, { className: "PrismRendererComponent", filePath: "app/renderer/prism-renderer.component.ts", lineNumber: 155 }); })();
258
261
  function parseContentToNodes(content) {
259
262
  if (typeof content === 'string') {
260
263
  return [htmlToNodes(content)];
@@ -1 +1 @@
1
- {"version":3,"file":"snippet-generator.d.ts","sourceRoot":"","sources":["../../../src/app/renderer/snippet-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAEvE,MAAM,WAAW,uBAAuB;IACtC,IAAI,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC;CAC/B;AAgBD,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,SAAS,EAAE,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,YAAY,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,EAClC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACzC,gBAAgB,CAAC,EAAE,uBAAuB,GACzC,MAAM,CA2DR"}
1
+ {"version":3,"file":"snippet-generator.d.ts","sourceRoot":"","sources":["../../../src/app/renderer/snippet-generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAEvE,MAAM,WAAW,uBAAuB;IACtC,IAAI,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC;CAC/B;AAoBD,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,SAAS,EAAE,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,YAAY,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,EAClC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACzC,gBAAgB,CAAC,EAAE,uBAAuB,GACzC,MAAM,CA2DR"}
@@ -1,6 +1,9 @@
1
+ function escapeAttrValue(value) {
2
+ return String(value).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
3
+ }
1
4
  function pushAttribute(attributes, name, value) {
2
5
  if (typeof value === 'string') {
3
- attributes.push(`${name}="${value}"`);
6
+ attributes.push(`${name}="${escapeAttrValue(value)}"`);
4
7
  }
5
8
  else if (typeof value === 'boolean') {
6
9
  attributes.push(`[${name}]="true"`);
@@ -12,7 +15,7 @@ function pushAttribute(attributes, name, value) {
12
15
  attributes.push(`[${name}]="yourData"`);
13
16
  }
14
17
  else {
15
- attributes.push(`[${name}]="${String(value)}"`);
18
+ attributes.push(`[${name}]="${escapeAttrValue(value)}"`);
16
19
  }
17
20
  }
18
21
  export function generateSnippet(selector, inputs, values, explicitKeys, content, directiveOptions) {
@@ -92,7 +95,7 @@ function generateDirectiveSnippet(selector, inputs, values, explicitKeys, conten
92
95
  tag = host.selector;
93
96
  if (host.inputs) {
94
97
  for (const [k, v] of Object.entries(host.inputs)) {
95
- hostAttrs.push(`${k}="${String(v)}"`);
98
+ hostAttrs.push(`${k}="${escapeAttrValue(v)}"`);
96
99
  }
97
100
  }
98
101
  }
@@ -0,0 +1,20 @@
1
+ import * as i0 from "@angular/core";
2
+ export declare class PrismPersistenceService {
3
+ private readonly config;
4
+ private readonly navigationService;
5
+ private readonly rendererService;
6
+ private readonly a11yPanelState;
7
+ private readonly a11yPerspective;
8
+ private readonly injector;
9
+ private suppressSync;
10
+ private writeTimer;
11
+ private lastSerialized;
12
+ init(): void;
13
+ private restoreFromStorage;
14
+ private setupSyncEffect;
15
+ private writeDebounced;
16
+ private serialize;
17
+ static ɵfac: i0.ɵɵFactoryDeclaration<PrismPersistenceService, never>;
18
+ static ɵprov: i0.ɵɵInjectableDeclaration<PrismPersistenceService>;
19
+ }
20
+ //# sourceMappingURL=prism-persistence.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prism-persistence.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-persistence.service.ts"],"names":[],"mappings":";AAqBA,qBACa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAiE;IACxF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAkC;IACpE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiC;IAChE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkC;IAClE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAE7C,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,UAAU,CAA8C;IAChE,OAAO,CAAC,cAAc,CAAuB;IAE7C,IAAI,IAAI,IAAI;IAOZ,OAAO,CAAC,kBAAkB;IA6C1B,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,cAAc;IAgBtB,OAAO,CAAC,SAAS;yCA7FN,uBAAuB;6CAAvB,uBAAuB;CAwHnC"}
@@ -0,0 +1,139 @@
1
+ import { effect, inject, Injectable, Injector } from '@angular/core';
2
+ import { PRISM_CONFIG } from '../tokens/prism-tokens.js';
3
+ import { A11yPanelStateService } from '../panels/a11y/a11y-panel-state.service.js';
4
+ import { A11yPerspectiveService } from '../panels/a11y/a11y-perspective.service.js';
5
+ import { PrismNavigationService } from './prism-navigation.service.js';
6
+ import { PrismRendererService } from './prism-renderer.service.js';
7
+ import * as i0 from "@angular/core";
8
+ const STORAGE_KEY = 'ng-prism:state';
9
+ const SCHEMA_VERSION = 1;
10
+ const DEBOUNCE_MS = 200;
11
+ export class PrismPersistenceService {
12
+ config = inject(PRISM_CONFIG, { optional: true }) ?? {};
13
+ navigationService = inject(PrismNavigationService);
14
+ rendererService = inject(PrismRendererService);
15
+ a11yPanelState = inject(A11yPanelStateService);
16
+ a11yPerspective = inject(A11yPerspectiveService);
17
+ injector = inject(Injector);
18
+ suppressSync = false;
19
+ writeTimer = null;
20
+ lastSerialized = null;
21
+ init() {
22
+ if (this.config.persistState === false)
23
+ return;
24
+ const { raw } = this.restoreFromStorage();
25
+ if (raw !== null)
26
+ this.lastSerialized = raw;
27
+ this.setupSyncEffect();
28
+ }
29
+ restoreFromStorage() {
30
+ let parsed = null;
31
+ let raw = null;
32
+ try {
33
+ raw = sessionStorage.getItem(STORAGE_KEY);
34
+ if (raw)
35
+ parsed = JSON.parse(raw);
36
+ }
37
+ catch {
38
+ parsed = null;
39
+ raw = null;
40
+ }
41
+ if (!parsed || parsed.version !== SCHEMA_VERSION)
42
+ return { raw: null };
43
+ this.suppressSync = true;
44
+ try {
45
+ const a11yTabs = ['violations', 'keyboard', 'tree', 'sr'];
46
+ if (parsed.a11y?.activeTab && a11yTabs.includes(parsed.a11y.activeTab)) {
47
+ this.a11yPanelState.activeTab.set(parsed.a11y.activeTab);
48
+ }
49
+ if (parsed.a11y?.perspective === 'visual' || parsed.a11y?.perspective === 'screen-reader') {
50
+ this.a11yPerspective.mode.set(parsed.a11y.perspective);
51
+ }
52
+ const activeComp = this.navigationService.activeComponent();
53
+ if (activeComp && parsed.inputs) {
54
+ const bucket = parsed.inputs[activeComp.meta.className];
55
+ if (bucket && bucket.variantIndex === this.rendererService.activeVariantIndex()) {
56
+ const validKeys = new Set(activeComp.meta.inputs.map((i) => i.name));
57
+ const filtered = {};
58
+ for (const [k, v] of Object.entries(bucket.values)) {
59
+ if (validKeys.has(k) || k === '__prismContent__')
60
+ filtered[k] = v;
61
+ }
62
+ if (Object.keys(filtered).length > 0) {
63
+ this.rendererService.inputValues.set({
64
+ ...this.rendererService.inputValues(),
65
+ ...filtered,
66
+ });
67
+ }
68
+ }
69
+ }
70
+ }
71
+ finally {
72
+ this.suppressSync = false;
73
+ }
74
+ return { raw };
75
+ }
76
+ setupSyncEffect() {
77
+ effect(() => {
78
+ this.navigationService.activeComponent();
79
+ this.rendererService.activeVariantIndex();
80
+ this.rendererService.inputValues();
81
+ this.a11yPanelState.activeTab();
82
+ this.a11yPerspective.mode();
83
+ if (this.suppressSync)
84
+ return;
85
+ this.writeDebounced();
86
+ }, { injector: this.injector });
87
+ }
88
+ writeDebounced() {
89
+ if (this.writeTimer)
90
+ clearTimeout(this.writeTimer);
91
+ this.writeTimer = setTimeout(() => {
92
+ this.writeTimer = null;
93
+ const state = this.serialize();
94
+ const next = JSON.stringify(state);
95
+ if (next === this.lastSerialized)
96
+ return;
97
+ try {
98
+ sessionStorage.setItem(STORAGE_KEY, next);
99
+ this.lastSerialized = next;
100
+ }
101
+ catch (err) {
102
+ console.warn('[ng-prism] Failed to persist state:', err);
103
+ }
104
+ }, DEBOUNCE_MS);
105
+ }
106
+ serialize() {
107
+ const activeComp = this.navigationService.activeComponent();
108
+ const variantIndex = this.rendererService.activeVariantIndex();
109
+ const values = this.rendererService.inputValues();
110
+ let inputs = {};
111
+ if (this.lastSerialized) {
112
+ try {
113
+ const prev = JSON.parse(this.lastSerialized);
114
+ if (prev.version === SCHEMA_VERSION)
115
+ inputs = { ...prev.inputs };
116
+ }
117
+ catch {
118
+ inputs = {};
119
+ }
120
+ }
121
+ if (activeComp) {
122
+ inputs[activeComp.meta.className] = { variantIndex, values: { ...values } };
123
+ }
124
+ return {
125
+ version: SCHEMA_VERSION,
126
+ inputs,
127
+ a11y: {
128
+ activeTab: this.a11yPanelState.activeTab(),
129
+ perspective: this.a11yPerspective.mode(),
130
+ },
131
+ };
132
+ }
133
+ static ɵfac = function PrismPersistenceService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PrismPersistenceService)(); };
134
+ static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: PrismPersistenceService, factory: PrismPersistenceService.ɵfac, providedIn: 'root' });
135
+ }
136
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PrismPersistenceService, [{
137
+ type: Injectable,
138
+ args: [{ providedIn: 'root' }]
139
+ }], null, null); })();
@@ -1 +1 @@
1
- {"version":3,"file":"prism-renderer.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-renderer.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;;AAGrE,qBACa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAkC;IAEpE,QAAQ,CAAC,kBAAkB,iDAAa;IACxC,QAAQ,CAAC,WAAW,kEAAuC;IAC3D,QAAQ,CAAC,aAAa,sFAAkE;IACxF,QAAQ,CAAC,eAAe,yDAAgC;IAExD,OAAO,CAAC,cAAc,CAAuB;IAE7C,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI;IAM/C,qBAAqB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI;IAoCnD,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAMlC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAI/C,OAAO,CAAC,YAAY;yCA9DT,oBAAoB;6CAApB,oBAAoB;CAuGhC"}
1
+ {"version":3,"file":"prism-renderer.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-renderer.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;;AAGrE,qBACa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAkC;IAEpE,QAAQ,CAAC,kBAAkB,iDAAa;IACxC,QAAQ,CAAC,WAAW,kEAAuC;IAC3D,QAAQ,CAAC,aAAa,sFAAkE;IACxF,QAAQ,CAAC,eAAe,yDAAgC;IAExD,OAAO,CAAC,cAAc,CAAuB;IAE7C,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI;IAM/C,qBAAqB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI;IA2CnD,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAMlC,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAI/C,OAAO,CAAC,YAAY;yCArET,oBAAoB;6CAApB,oBAAoB;CA8GhC"}
@@ -16,7 +16,7 @@ export class PrismRendererService {
16
16
  reconcileForComponent(comp) {
17
17
  const prev = this._lastClassName;
18
18
  this._lastClassName = comp.meta.className;
19
- if (prev !== comp.meta.className) {
19
+ if (prev !== null && prev !== comp.meta.className) {
20
20
  this.activeVariantIndex.set(0);
21
21
  this.applyVariant(0, comp);
22
22
  return;
@@ -25,6 +25,12 @@ export class PrismRendererService {
25
25
  const maxIndex = Math.max(0, variants.length - 1);
26
26
  const preservedIndex = Math.min(this.activeVariantIndex(), maxIndex);
27
27
  this.activeVariantIndex.set(preservedIndex);
28
+ const defaults = {};
29
+ for (const input of comp.meta.inputs) {
30
+ if (input.defaultValue !== undefined) {
31
+ defaults[input.name] = input.defaultValue;
32
+ }
33
+ }
28
34
  const validKeys = new Set(comp.meta.inputs.map((i) => i.name));
29
35
  const currentValues = this.inputValues();
30
36
  const preserved = {};
@@ -35,7 +41,7 @@ export class PrismRendererService {
35
41
  }
36
42
  const variant = variants[preservedIndex];
37
43
  const variantInputs = variant?.inputs ?? {};
38
- const merged = { ...variantInputs, ...preserved };
44
+ const merged = { ...defaults, ...variantInputs, ...preserved };
39
45
  if (comp.meta.componentMeta.isDirective && variant?.content && preserved['__prismContent__'] === undefined) {
40
46
  merged['__prismContent__'] = typeof variant.content === 'string' ? variant.content : '';
41
47
  }
@@ -1 +1 @@
1
- {"version":3,"file":"prism-url-state.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-url-state.service.ts"],"names":[],"mappings":";AAcA,qBACa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAkC;IACpE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAC/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAE7C,OAAO,CAAC,YAAY,CAAS;IAE7B,IAAI,IAAI,IAAI;IAiBZ,OAAO,CAAC,cAAc;IAsCtB,OAAO,CAAC,UAAU;yCAjEP,oBAAoB;6CAApB,oBAAoB;CAsGhC"}
1
+ {"version":3,"file":"prism-url-state.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-url-state.service.ts"],"names":[],"mappings":";AAgBA,qBACa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAkC;IACpE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwB;IAC/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAE7C,OAAO,CAAC,YAAY,CAAS;IAE7B,IAAI,IAAI,IAAI;IAkBZ,OAAO,CAAC,cAAc;IA0CtB,OAAO,CAAC,UAAU;yCAtEP,oBAAoB;6CAApB,oBAAoB;CA+GhC"}
@@ -10,6 +10,8 @@ const PARAM_PAGE = 'page';
10
10
  const PARAM_VARIANT = 'variant';
11
11
  const PARAM_VIEW = 'view';
12
12
  const DEFAULT_VIEW = 'renderer';
13
+ const PARAM_PANEL = 'panel';
14
+ const DEFAULT_PANEL = 'controls';
13
15
  export class PrismUrlStateService {
14
16
  manifestService = inject(PrismManifestService);
15
17
  navigationService = inject(PrismNavigationService);
@@ -26,9 +28,10 @@ export class PrismUrlStateService {
26
28
  const item = this.navigationService.activeItem();
27
29
  const variantIndex = this.rendererService.activeVariantIndex();
28
30
  const viewId = this.panelService.activeViewId();
31
+ const panelId = this.panelService.activePanelId();
29
32
  if (this.suppressSync)
30
33
  return;
31
- this.writeToUrl(item, variantIndex, viewId);
34
+ this.writeToUrl(item, variantIndex, viewId, panelId);
32
35
  }, { injector: this.injector });
33
36
  window.addEventListener('popstate', () => this.restoreFromUrl());
34
37
  }
@@ -38,6 +41,7 @@ export class PrismUrlStateService {
38
41
  const pageTitle = params.get(PARAM_PAGE);
39
42
  const variantParam = params.get(PARAM_VARIANT);
40
43
  const viewId = params.get(PARAM_VIEW);
44
+ const panelId = params.get(PARAM_PANEL);
41
45
  this.suppressSync = true;
42
46
  try {
43
47
  if (componentClassName) {
@@ -64,12 +68,15 @@ export class PrismUrlStateService {
64
68
  if (viewId) {
65
69
  this.panelService.activeViewId.set(viewId);
66
70
  }
71
+ if (panelId) {
72
+ this.panelService.activePanelId.set(panelId);
73
+ }
67
74
  }
68
75
  finally {
69
76
  this.suppressSync = false;
70
77
  }
71
78
  }
72
- writeToUrl(item, variantIndex, viewId) {
79
+ writeToUrl(item, variantIndex, viewId, panelId) {
73
80
  const params = new URLSearchParams();
74
81
  if (item?.kind === 'component') {
75
82
  params.set(PARAM_COMPONENT, item.data.meta.className);
@@ -83,6 +90,9 @@ export class PrismUrlStateService {
83
90
  if (viewId && viewId !== DEFAULT_VIEW) {
84
91
  params.set(PARAM_VIEW, viewId);
85
92
  }
93
+ if (panelId && panelId !== DEFAULT_PANEL) {
94
+ params.set(PARAM_PANEL, panelId);
95
+ }
86
96
  const queryString = params.toString();
87
97
  const newUrl = queryString
88
98
  ? `${window.location.pathname}?${queryString}`
@@ -10,6 +10,7 @@ export declare class PrismShellComponent {
10
10
  protected readonly panelService: PrismPanelService;
11
11
  private readonly pluginService;
12
12
  private readonly urlStateService;
13
+ private readonly persistenceService;
13
14
  protected readonly viewPanels: import("@angular/core").Signal<import("../../plugin/plugin.types.js").PanelDefinition[]>;
14
15
  protected readonly showPanel: import("@angular/core").Signal<boolean>;
15
16
  protected readonly shellStyle: import("@angular/core").Signal<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"prism-shell.component.d.ts","sourceRoot":"","sources":["../../../src/app/shell/prism-shell.component.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;;AAiBvE,qBA2Pa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuC;IAC9D,SAAS,CAAC,QAAQ,CAAC,iBAAiB,yBAAkC;IACtE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,SAAS,CAAC,QAAQ,CAAC,MAAM,qBAA8B;IACvD,SAAS,CAAC,QAAQ,CAAC,YAAY,oBAA6B;IAC5D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA8B;IAC5D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAEhE,SAAS,CAAC,QAAQ,CAAC,UAAU,2FAG1B;IAEH,SAAS,CAAC,QAAQ,CAAC,SAAS,0CAK1B;IAEF,SAAS,CAAC,QAAQ,CAAC,UAAU,yCAG1B;;IAgCH,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI;yCAxDhC,mBAAmB;2CAAnB,mBAAmB;CAyE/B"}
1
+ {"version":3,"file":"prism-shell.component.d.ts","sourceRoot":"","sources":["../../../src/app/shell/prism-shell.component.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;;AAkBvE,qBA2Pa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAuC;IAC9D,SAAS,CAAC,QAAQ,CAAC,iBAAiB,yBAAkC;IACtE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,SAAS,CAAC,QAAQ,CAAC,MAAM,qBAA8B;IACvD,SAAS,CAAC,QAAQ,CAAC,YAAY,oBAA6B;IAC5D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA8B;IAC5D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAmC;IAEtE,SAAS,CAAC,QAAQ,CAAC,UAAU,2FAG1B;IAEH,SAAS,CAAC,QAAQ,CAAC,SAAS,0CAK1B;IAEF,SAAS,CAAC,QAAQ,CAAC,UAAU,yCAG1B;;IAiCH,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,aAAa,GAAG,IAAI;yCA1DhC,mBAAmB;2CAAnB,mBAAmB;CA2E/B"}
@@ -14,6 +14,7 @@ import { PrismSidebarComponent } from '../sidebar/prism-sidebar.component.js';
14
14
  import { PrismPageRendererComponent } from '../page-renderer/prism-page-renderer.component.js';
15
15
  import { BUILTIN_PANELS } from '../panels/builtin-panels.js';
16
16
  import { PrismUrlStateService } from '../services/prism-url-state.service.js';
17
+ import { PrismPersistenceService } from '../services/prism-persistence.service.js';
17
18
  import { PrismViewTabBarComponent } from '../view-tab-bar/prism-view-tab-bar.component.js';
18
19
  import { PrismViewPanelHostComponent } from '../view-tab-bar/prism-view-panel-host.component.js';
19
20
  import { PrismResizerDirective } from '../directives/prism-resizer.directive.js';
@@ -105,6 +106,7 @@ export class PrismShellComponent {
105
106
  panelService = inject(PrismPanelService);
106
107
  pluginService = inject(PrismPluginService);
107
108
  urlStateService = inject(PrismUrlStateService);
109
+ persistenceService = inject(PrismPersistenceService);
108
110
  viewPanels = computed(() => [
109
111
  ...BUILTIN_PANELS.filter((p) => p.placement === 'view'),
110
112
  ...this.pluginService.viewPanels(),
@@ -120,6 +122,7 @@ export class PrismShellComponent {
120
122
  this.themeService.applyConfigOverrides(this.config);
121
123
  this.navigationService.selectFirst();
122
124
  this.urlStateService.init();
125
+ this.persistenceService.init();
123
126
  let lastItemKey = null;
124
127
  effect(() => {
125
128
  const item = this.navigationService.activeItem();
@@ -309,4 +312,4 @@ export class PrismShellComponent {
309
312
  type: HostListener,
310
313
  args: ['document:keydown', ['$event']]
311
314
  }] }); })();
312
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismShellComponent, { className: "PrismShellComponent", filePath: "app/shell/prism-shell.component.ts", lineNumber: 283 }); })();
315
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismShellComponent, { className: "PrismShellComponent", filePath: "app/shell/prism-shell.component.ts", lineNumber: 284 }); })();
@@ -1 +1 @@
1
- {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../../src/builder/config-loader/config-loader.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,CAAC;AAK3B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAID,wBAAsB,UAAU,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CA6BrF"}
1
+ {"version":3,"file":"config-loader.d.ts","sourceRoot":"","sources":["../../../src/builder/config-loader/config-loader.ts"],"names":[],"mappings":"AAAA,OAAO,mBAAmB,CAAC;AAK3B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAElE,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAID,wBAAsB,UAAU,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAsCrF"}
@@ -1,12 +1,17 @@
1
1
  import '@angular/compiler';
2
2
  import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'fs';
3
- import { join } from 'path';
3
+ import { resolve, sep } from 'path';
4
4
  import { pathToFileURL } from 'url';
5
5
  import ts from 'typescript';
6
6
  const DEFAULT_CONFIG_FILE = 'ng-prism.config.ts';
7
7
  export async function loadConfig(options) {
8
8
  const configFileName = options.configFileName ?? DEFAULT_CONFIG_FILE;
9
- const configPath = join(options.workspaceRoot, configFileName);
9
+ const workspaceRoot = resolve(options.workspaceRoot);
10
+ const configPath = resolve(workspaceRoot, configFileName);
11
+ if (configPath !== workspaceRoot && !configPath.startsWith(workspaceRoot + sep)) {
12
+ throw new Error(`ng-prism: configFile "${configFileName}" resolves outside workspace root (${workspaceRoot}). ` +
13
+ `Provide a path relative to the workspace.`);
14
+ }
10
15
  if (!existsSync(configPath)) {
11
16
  return {};
12
17
  }
@@ -19,7 +24,8 @@ export async function loadConfig(options) {
19
24
  esModuleInterop: true,
20
25
  },
21
26
  });
22
- const tempPath = configPath.replace(/\.ts$/, '.tmp.mjs');
27
+ const uniqueSuffix = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
28
+ const tempPath = configPath.replace(/\.ts$/, `.${uniqueSuffix}.tmp.mjs`);
23
29
  try {
24
30
  writeFileSync(tempPath, transpiled.outputText, 'utf-8');
25
31
  const module = await import(pathToFileURL(tempPath).href);
@@ -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;AAGnG,wBAAsB,cAAc,CAClC,QAAQ,EAAE,aAAa,EACvB,OAAO,EAAE,aAAa,EAAE,GACvB,OAAO,CAAC,aAAa,CAAC,CA2CxB"}
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,25 +1,46 @@
1
+ function pluginLabel(plugin) {
2
+ return plugin.name ? `"${plugin.name}"` : '<unnamed>';
3
+ }
4
+ function wrapPluginError(plugin, hook, target, err) {
5
+ const cause = err instanceof Error ? err.message : String(err);
6
+ const wrapped = new Error(`ng-prism: plugin ${pluginLabel(plugin)} failed in ${hook} for ${target} — ${cause}`, err instanceof Error ? { cause: err } : undefined);
7
+ if (err instanceof Error && err.stack) {
8
+ wrapped.stack = `${wrapped.message}\nCaused by: ${err.stack}`;
9
+ }
10
+ return wrapped;
11
+ }
1
12
  export async function runPluginHooks(manifest, plugins) {
2
- let components = [...manifest.components];
3
- for (const comp of components) {
4
- let current = comp;
13
+ const components = [...manifest.components];
14
+ for (let i = 0; i < components.length; i++) {
15
+ let current = components[i];
5
16
  for (const plugin of plugins) {
6
17
  if (plugin.onComponentScanned) {
7
- const result = await plugin.onComponentScanned(current);
8
- if (result) {
9
- current = result;
18
+ try {
19
+ const result = await plugin.onComponentScanned(current);
20
+ if (result) {
21
+ current = result;
22
+ }
23
+ }
24
+ catch (err) {
25
+ throw wrapPluginError(plugin, 'onComponentScanned', `component "${current.className}"`, err);
10
26
  }
11
27
  }
12
28
  }
13
- components[components.indexOf(comp)] = current;
29
+ components[i] = current;
14
30
  }
15
- let pages = [...(manifest.pages ?? [])];
31
+ const pages = [...(manifest.pages ?? [])];
16
32
  for (let i = 0; i < pages.length; i++) {
17
33
  let current = pages[i];
18
34
  for (const plugin of plugins) {
19
35
  if (plugin.onPageScanned) {
20
- const result = await plugin.onPageScanned(current);
21
- if (result) {
22
- current = result;
36
+ try {
37
+ const result = await plugin.onPageScanned(current);
38
+ if (result) {
39
+ current = result;
40
+ }
41
+ }
42
+ catch (err) {
43
+ throw wrapPluginError(plugin, 'onPageScanned', `page "${current.title}"`, err);
23
44
  }
24
45
  }
25
46
  }
@@ -28,9 +49,14 @@ export async function runPluginHooks(manifest, plugins) {
28
49
  let result = { components, pages };
29
50
  for (const plugin of plugins) {
30
51
  if (plugin.onManifestReady) {
31
- const updated = await plugin.onManifestReady(result);
32
- if (updated) {
33
- result = updated;
52
+ try {
53
+ const updated = await plugin.onManifestReady(result);
54
+ if (updated) {
55
+ result = updated;
56
+ }
57
+ }
58
+ catch (err) {
59
+ throw wrapPluginError(plugin, 'onManifestReady', 'manifest', err);
34
60
  }
35
61
  }
36
62
  }
@@ -16,14 +16,14 @@ export function scanComponents(exports, checker) {
16
16
  const showcaseDecorator = findDecorator(classDecl, 'Showcase');
17
17
  if (!showcaseDecorator)
18
18
  continue;
19
- const showcaseConfig = extractShowcaseConfig(showcaseDecorator);
19
+ const className = classDecl.name?.text ?? 'Anonymous';
20
+ const showcaseConfig = extractShowcaseConfig(showcaseDecorator, className);
20
21
  if (!showcaseConfig)
21
22
  continue;
22
23
  const componentMeta = extractComponentMeta(classDecl);
23
24
  const inputs = extractInputs(classDecl, checker);
24
25
  const outputs = extractOutputs(classDecl, checker);
25
26
  const filePath = classDecl.getSourceFile().fileName;
26
- const className = classDecl.name?.text ?? 'Anonymous';
27
27
  if (hasDecoratorInputs(classDecl)) {
28
28
  console.warn(`⚠ ng-prism: ${className} uses @Input() decorators which are not fully supported. ` +
29
29
  `Migrate to input() signals for full ng-prism support.`);
@@ -48,13 +48,18 @@ function hasDecoratorInputs(classDecl) {
48
48
  }
49
49
  return false;
50
50
  }
51
- function extractShowcaseConfig(decorator) {
51
+ function extractShowcaseConfig(decorator, className) {
52
52
  const arg = getDecoratorArgument(decorator);
53
53
  if (!arg)
54
54
  return undefined;
55
55
  const raw = evaluateExpression(arg);
56
- if (!raw || typeof raw !== 'object' || !('title' in raw))
56
+ if (!raw || typeof raw !== 'object')
57
57
  return undefined;
58
+ if (!('title' in raw)) {
59
+ console.warn(`⚠ ng-prism: ${className} has @Showcase without a "title" field — skipping. ` +
60
+ `Add a title so it can appear in the styleguide.`);
61
+ return undefined;
62
+ }
58
63
  const obj = raw;
59
64
  const config = {
60
65
  title: obj['title'],
@@ -6,6 +6,24 @@ export function discoverSecondaryEntryPoints(libraryRoot, baseImportPath) {
6
6
  return entryPoints;
7
7
  }
8
8
  function findNgPackageJsons(dir, libraryRoot, baseImportPath, result) {
9
+ const isLibraryRoot = dir === libraryRoot;
10
+ const ngPackagePath = join(dir, 'ng-package.json');
11
+ if (existsSync(ngPackagePath)) {
12
+ const ngPackage = JSON.parse(readFileSync(ngPackagePath, 'utf-8'));
13
+ if (!ngPackage.dest) {
14
+ const entryFile = ngPackage.lib?.entryFile ?? 'public-api.ts';
15
+ const entryFilePath = join(dir, entryFile);
16
+ if (existsSync(entryFilePath)) {
17
+ const relDir = relative(libraryRoot, dir);
18
+ const importPath = relDir === ''
19
+ ? baseImportPath
20
+ : posix.join(baseImportPath, relDir.split('\\').join('/'));
21
+ result.push({ entryFile: entryFilePath, importPath });
22
+ }
23
+ if (!isLibraryRoot)
24
+ return;
25
+ }
26
+ }
9
27
  let entries;
10
28
  try {
11
29
  entries = readdirSync(dir);
@@ -17,22 +35,13 @@ function findNgPackageJsons(dir, libraryRoot, baseImportPath, result) {
17
35
  if (entry === 'node_modules' || entry === '.git')
18
36
  continue;
19
37
  const fullPath = join(dir, entry);
20
- if (!statSync(fullPath).isDirectory())
21
- continue;
22
- const ngPackagePath = join(fullPath, 'ng-package.json');
23
- if (!existsSync(ngPackagePath)) {
24
- findNgPackageJsons(fullPath, libraryRoot, baseImportPath, result);
25
- continue;
38
+ try {
39
+ if (!statSync(fullPath).isDirectory())
40
+ continue;
26
41
  }
27
- const ngPackage = JSON.parse(readFileSync(ngPackagePath, 'utf-8'));
28
- if (ngPackage.dest)
42
+ catch {
29
43
  continue;
30
- const entryFile = ngPackage.lib?.entryFile ?? 'public-api.ts';
31
- const entryFilePath = join(fullPath, entryFile);
32
- if (!existsSync(entryFilePath))
33
- continue;
34
- const relDir = relative(libraryRoot, fullPath);
35
- const importPath = posix.join(baseImportPath, relDir.split('\\').join('/'));
36
- result.push({ entryFile: entryFilePath, importPath });
44
+ }
45
+ findNgPackageJsons(fullPath, libraryRoot, baseImportPath, result);
37
46
  }
38
47
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/builder/serve/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,OAAO,EAA2C,MAAM,2BAA2B,CAAC;AACjH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAGjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AA0DtD,QAAA,MAAM,OAAO,EAAE,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,UAAU,CAAyD,CAAC;AACrH,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/builder/serve/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,OAAO,EAA2C,MAAM,2BAA2B,CAAC;AACjH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAGjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AA+DtD,QAAA,MAAM,OAAO,EAAE,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,UAAU,CAAyD,CAAC;AACrH,eAAe,OAAO,CAAC"}