@ng-prism/core 21.9.1 → 21.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/app/canvas/prism-canvas-bg-pill.component.d.ts +10 -0
  2. package/dist/app/canvas/prism-canvas-bg-pill.component.d.ts.map +1 -0
  3. package/dist/app/canvas/prism-canvas-bg-pill.component.js +51 -0
  4. package/dist/app/canvas/prism-canvas-toolbar.component.d.ts +3 -0
  5. package/dist/app/canvas/prism-canvas-toolbar.component.d.ts.map +1 -1
  6. package/dist/app/canvas/prism-canvas-toolbar.component.js +57 -17
  7. package/dist/app/canvas/prism-code-drawer.component.d.ts +2 -0
  8. package/dist/app/canvas/prism-code-drawer.component.d.ts.map +1 -1
  9. package/dist/app/canvas/prism-code-drawer.component.js +6 -4
  10. package/dist/app/renderer/prism-renderer.component.d.ts +2 -0
  11. package/dist/app/renderer/prism-renderer.component.d.ts.map +1 -1
  12. package/dist/app/renderer/prism-renderer.component.js +25 -15
  13. package/dist/app/services/prism-canvas.service.d.ts +2 -1
  14. package/dist/app/services/prism-canvas.service.d.ts.map +1 -1
  15. package/dist/app/services/prism-canvas.service.js +2 -1
  16. package/dist/app/services/prism-layout.service.d.ts +2 -0
  17. package/dist/app/services/prism-layout.service.d.ts.map +1 -1
  18. package/dist/app/services/prism-layout.service.js +14 -5
  19. package/dist/app/services/prism-theme.service.d.ts +1 -1
  20. package/dist/app/services/prism-variant-bg.service.d.ts +19 -0
  21. package/dist/app/services/prism-variant-bg.service.d.ts.map +1 -0
  22. package/dist/app/services/prism-variant-bg.service.js +45 -0
  23. package/dist/builder/scanner/component.scanner.d.ts.map +1 -1
  24. package/dist/builder/scanner/component.scanner.js +25 -5
  25. package/dist/decorator/showcase.types.d.ts +10 -0
  26. package/dist/decorator/showcase.types.d.ts.map +1 -1
  27. package/dist/shared/canvas-bg.type.d.ts +3 -0
  28. package/dist/shared/canvas-bg.type.d.ts.map +1 -0
  29. package/dist/shared/canvas-bg.type.js +7 -0
  30. package/dist/transformer/showcase-strip.transformer.d.ts.map +1 -1
  31. package/dist/transformer/showcase-strip.transformer.js +49 -12
  32. package/package.json +1 -1
@@ -0,0 +1,10 @@
1
+ import * as i0 from "@angular/core";
2
+ export declare class PrismCanvasBgPillComponent {
3
+ private readonly variantBg;
4
+ protected readonly recommended: import("@angular/core").Signal<import("../services/prism-canvas.service.js").CanvasBg | null>;
5
+ protected readonly visible: import("@angular/core").Signal<boolean>;
6
+ protected reset(): void;
7
+ static ɵfac: i0.ɵɵFactoryDeclaration<PrismCanvasBgPillComponent, never>;
8
+ static ɵcmp: i0.ɵɵComponentDeclaration<PrismCanvasBgPillComponent, "prism-canvas-bg-pill", never, {}, {}, never, never, true, never>;
9
+ }
10
+ //# sourceMappingURL=prism-canvas-bg-pill.component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prism-canvas-bg-pill.component.d.ts","sourceRoot":"","sources":["../../../src/app/canvas/prism-canvas-bg-pill.component.ts"],"names":[],"mappings":";AAGA,qBAkEa,0BAA0B;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;IAE3D,SAAS,CAAC,QAAQ,CAAC,WAAW,gGAA8B;IAC5D,SAAS,CAAC,QAAQ,CAAC,OAAO,0CAA8B;IAExD,SAAS,CAAC,KAAK,IAAI,IAAI;yCANZ,0BAA0B;2CAA1B,0BAA0B;CAStC"}
@@ -0,0 +1,51 @@
1
+ import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
2
+ import { PrismVariantBgService } from '../services/prism-variant-bg.service.js';
3
+ import * as i0 from "@angular/core";
4
+ function PrismCanvasBgPillComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
5
+ const _r1 = i0.ɵɵgetCurrentView();
6
+ i0.ɵɵdomElementStart(0, "div", 0)(1, "span", 1);
7
+ i0.ɵɵtext(2);
8
+ i0.ɵɵdomElementEnd();
9
+ i0.ɵɵdomElementStart(3, "button", 2);
10
+ i0.ɵɵdomListener("click", function PrismCanvasBgPillComponent_Conditional_0_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.reset()); });
11
+ i0.ɵɵtext(4, " Reset ");
12
+ i0.ɵɵdomElementEnd()();
13
+ } if (rf & 2) {
14
+ const ctx_r1 = i0.ɵɵnextContext();
15
+ i0.ɵɵadvance(2);
16
+ i0.ɵɵtextInterpolate1("Recommended: ", ctx_r1.recommended());
17
+ } }
18
+ export class PrismCanvasBgPillComponent {
19
+ variantBg = inject(PrismVariantBgService);
20
+ recommended = this.variantBg.recommended;
21
+ visible = this.variantBg.isDeviating;
22
+ reset() {
23
+ this.variantBg.clearOverride();
24
+ }
25
+ static ɵfac = function PrismCanvasBgPillComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PrismCanvasBgPillComponent)(); };
26
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PrismCanvasBgPillComponent, selectors: [["prism-canvas-bg-pill"]], decls: 1, vars: 1, consts: [[1, "prism-bg-pill"], [1, "label"], ["type", "button", "title", "Reset to recommended background", "aria-label", "Reset to recommended background", 1, "reset", 3, "click"]], template: function PrismCanvasBgPillComponent_Template(rf, ctx) { if (rf & 1) {
27
+ i0.ɵɵconditionalCreate(0, PrismCanvasBgPillComponent_Conditional_0_Template, 5, 1, "div", 0);
28
+ } if (rf & 2) {
29
+ i0.ɵɵconditional(ctx.visible() ? 0 : -1);
30
+ } }, styles: ["[_nghost-%COMP%] {\n position: absolute;\n top: 12px;\n right: 20px;\n pointer-events: none;\n z-index: 2;\n }\n .prism-bg-pill[_ngcontent-%COMP%] {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 4px 4px 4px 10px;\n border-radius: 999px;\n font-size: var(--fs-xs);\n font-family: var(--font-sans);\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 pointer-events: auto;\n animation: _ngcontent-%COMP%_fadeIn var(--dur-base) ease-out;\n }\n .label[_ngcontent-%COMP%] { letter-spacing: 0.02em; }\n .reset[_ngcontent-%COMP%] {\n height: 22px;\n padding: 0 10px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--prism-primary) 12%, transparent);\n color: var(--prism-text);\n border: 1px solid color-mix(in srgb, var(--prism-primary) 25%, transparent);\n font-family: inherit;\n font-size: inherit;\n font-weight: 500;\n cursor: pointer;\n transition: background var(--dur-fast);\n }\n .reset[_ngcontent-%COMP%]:hover {\n background: color-mix(in srgb, var(--prism-primary) 20%, transparent);\n }\n @keyframes _ngcontent-%COMP%_fadeIn {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n }"], changeDetection: 0 });
31
+ }
32
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PrismCanvasBgPillComponent, [{
33
+ type: Component,
34
+ args: [{ selector: 'prism-canvas-bg-pill', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
35
+ @if (visible()) {
36
+ <div class="prism-bg-pill">
37
+ <span class="label">Recommended: {{ recommended() }}</span>
38
+ <button
39
+ type="button"
40
+ class="reset"
41
+ title="Reset to recommended background"
42
+ aria-label="Reset to recommended background"
43
+ (click)="reset()"
44
+ >
45
+ Reset
46
+ </button>
47
+ </div>
48
+ }
49
+ `, styles: ["\n :host {\n position: absolute;\n top: 12px;\n right: 20px;\n pointer-events: none;\n z-index: 2;\n }\n .prism-bg-pill {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 4px 4px 4px 10px;\n border-radius: 999px;\n font-size: var(--fs-xs);\n font-family: var(--font-sans);\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 pointer-events: auto;\n animation: fadeIn var(--dur-base) ease-out;\n }\n .label { letter-spacing: 0.02em; }\n .reset {\n height: 22px;\n padding: 0 10px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--prism-primary) 12%, transparent);\n color: var(--prism-text);\n border: 1px solid color-mix(in srgb, var(--prism-primary) 25%, transparent);\n font-family: inherit;\n font-size: inherit;\n font-weight: 500;\n cursor: pointer;\n transition: background var(--dur-fast);\n }\n .reset:hover {\n background: color-mix(in srgb, var(--prism-primary) 20%, transparent);\n }\n @keyframes fadeIn {\n from { opacity: 0; transform: translateY(-4px); }\n to { opacity: 1; transform: translateY(0); }\n }\n "] }]
50
+ }], null, null); })();
51
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismCanvasBgPillComponent, { className: "PrismCanvasBgPillComponent", filePath: "app/canvas/prism-canvas-bg-pill.component.ts", lineNumber: 70 }); })();
@@ -1,7 +1,10 @@
1
1
  import { PrismCanvasService, type CanvasBg } from '../services/prism-canvas.service.js';
2
+ import { PrismVariantBgService } from '../services/prism-variant-bg.service.js';
2
3
  import * as i0 from "@angular/core";
3
4
  export declare class PrismCanvasToolbarComponent {
4
5
  protected readonly canvas: PrismCanvasService;
6
+ protected readonly variantBg: PrismVariantBgService;
7
+ protected setBg(bg: CanvasBg): void;
5
8
  protected readonly bgs: CanvasBg[];
6
9
  protected readonly zooms: {
7
10
  value: number;
@@ -1 +1 @@
1
- {"version":3,"file":"prism-canvas-toolbar.component.d.ts","sourceRoot":"","sources":["../../../src/app/canvas/prism-canvas-toolbar.component.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,kBAAkB,EAClB,KAAK,QAAQ,EACd,MAAM,qCAAqC,CAAC;;AAE7C,qBAwHa,2BAA2B;IACtC,SAAS,CAAC,QAAQ,CAAC,MAAM,qBAA8B;IAEvD,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,CAMhC;IACF,SAAS,CAAC,QAAQ,CAAC,KAAK;;;QAKtB;IAEF,SAAS,CAAC,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM;yCAjB5B,2BAA2B;2CAA3B,2BAA2B;CAoBvC"}
1
+ {"version":3,"file":"prism-canvas-toolbar.component.d.ts","sourceRoot":"","sources":["../../../src/app/canvas/prism-canvas-toolbar.component.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,kBAAkB,EAClB,KAAK,QAAQ,EACd,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;;AAEhF,qBAgJa,2BAA2B;IACtC,SAAS,CAAC,QAAQ,CAAC,MAAM,qBAA8B;IACvD,SAAS,CAAC,QAAQ,CAAC,SAAS,wBAAiC;IAE7D,SAAS,CAAC,KAAK,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI;IAQnC,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,EAAE,CAMhC;IACF,SAAS,CAAC,QAAQ,CAAC,KAAK;;;QAKtB;IAEF,SAAS,CAAC,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM;yCA1B5B,2BAA2B;2CAA3B,2BAA2B;CA6BvC"}
@@ -1,24 +1,35 @@
1
1
  import { Component, inject, ChangeDetectionStrategy } from '@angular/core';
2
2
  import { PrismIconComponent } from '../icons/prism-icon.component.js';
3
3
  import { PrismCanvasService, } from '../services/prism-canvas.service.js';
4
+ import { PrismVariantBgService } from '../services/prism-variant-bg.service.js';
4
5
  import * as i0 from "@angular/core";
5
6
  const _forTrack0 = ($index, $item) => $item.value;
7
+ function PrismCanvasToolbarComponent_For_5_Conditional_1_Template(rf, ctx) { if (rf & 1) {
8
+ i0.ɵɵnamespaceSVG();
9
+ i0.ɵɵelementStart(0, "svg", 11);
10
+ i0.ɵɵelement(1, "path", 12);
11
+ i0.ɵɵelementEnd();
12
+ } }
6
13
  function PrismCanvasToolbarComponent_For_5_Template(rf, ctx) { if (rf & 1) {
7
14
  const _r1 = i0.ɵɵgetCurrentView();
8
- i0.ɵɵelementStart(0, "button", 9);
9
- i0.ɵɵlistener("click", function PrismCanvasToolbarComponent_For_5_Template_button_click_0_listener() { const bg_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.canvas.setBg(bg_r2)); });
10
- i0.ɵɵtext(1);
15
+ i0.ɵɵelementStart(0, "button", 10);
16
+ i0.ɵɵlistener("click", function PrismCanvasToolbarComponent_For_5_Template_button_click_0_listener() { const bg_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.setBg(bg_r2)); });
17
+ i0.ɵɵconditionalCreate(1, PrismCanvasToolbarComponent_For_5_Conditional_1_Template, 2, 0, ":svg:svg", 11);
18
+ i0.ɵɵtext(2);
11
19
  i0.ɵɵelementEnd();
12
20
  } if (rf & 2) {
13
21
  const bg_r2 = ctx.$implicit;
14
22
  const ctx_r2 = i0.ɵɵnextContext();
15
- i0.ɵɵclassProp("tool-btn--active", ctx_r2.canvas.bg() === bg_r2);
23
+ i0.ɵɵclassProp("tool-btn--active", ctx_r2.variantBg.effective() === bg_r2)("tool-btn--recommended", ctx_r2.variantBg.recommended() === bg_r2);
24
+ i0.ɵɵproperty("title", ctx_r2.variantBg.recommended() === bg_r2 ? "Recommended background for this variant" : null);
25
+ i0.ɵɵadvance();
26
+ i0.ɵɵconditional(ctx_r2.variantBg.recommended() === bg_r2 ? 1 : -1);
16
27
  i0.ɵɵadvance();
17
28
  i0.ɵɵtextInterpolate1(" ", ctx_r2.capitalize(bg_r2), " ");
18
29
  } }
19
30
  function PrismCanvasToolbarComponent_For_11_Template(rf, ctx) { if (rf & 1) {
20
31
  const _r4 = i0.ɵɵgetCurrentView();
21
- i0.ɵɵelementStart(0, "button", 9);
32
+ i0.ɵɵelementStart(0, "button", 13);
22
33
  i0.ɵɵlistener("click", function PrismCanvasToolbarComponent_For_11_Template_button_click_0_listener() { const z_r5 = i0.ɵɵrestoreView(_r4).$implicit; const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.canvas.setZoom(z_r5.value)); });
23
34
  i0.ɵɵtext(1);
24
35
  i0.ɵɵelementEnd();
@@ -31,6 +42,15 @@ function PrismCanvasToolbarComponent_For_11_Template(rf, ctx) { if (rf & 1) {
31
42
  } }
32
43
  export class PrismCanvasToolbarComponent {
33
44
  canvas = inject(PrismCanvasService);
45
+ variantBg = inject(PrismVariantBgService);
46
+ setBg(bg) {
47
+ if (this.variantBg.recommended() !== null) {
48
+ this.variantBg.setOverride(bg);
49
+ }
50
+ else {
51
+ this.canvas.setBg(bg);
52
+ }
53
+ }
34
54
  bgs = [
35
55
  'dots',
36
56
  'plain',
@@ -48,29 +68,29 @@ export class PrismCanvasToolbarComponent {
48
68
  return s.charAt(0).toUpperCase() + s.slice(1);
49
69
  }
50
70
  static ɵfac = function PrismCanvasToolbarComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PrismCanvasToolbarComponent)(); };
51
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PrismCanvasToolbarComponent, selectors: [["prism-canvas-toolbar"]], decls: 19, vars: 6, consts: [[1, "canvas-toolbar"], [1, "tool-label"], [1, "tool-group"], [1, "tool-btn", 3, "tool-btn--active"], [1, "tool-sep"], ["title", "Toggle guides", "aria-label", "Toggle guides", 1, "tool-btn", 3, "click"], ["name", "crosshair", 3, "size"], ["title", "Toggle rulers", "aria-label", "Toggle rulers", 1, "tool-btn", 3, "click"], ["name", "move", 3, "size"], [1, "tool-btn", 3, "click"]], template: function PrismCanvasToolbarComponent_Template(rf, ctx) { if (rf & 1) {
71
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PrismCanvasToolbarComponent, selectors: [["prism-canvas-toolbar"]], decls: 19, vars: 6, consts: [[1, "canvas-toolbar"], [1, "tool-label"], [1, "tool-group"], [1, "tool-btn", 3, "tool-btn--active", "tool-btn--recommended", "title"], [1, "tool-sep"], [1, "tool-btn", 3, "tool-btn--active"], ["title", "Toggle guides", "aria-label", "Toggle guides", 1, "tool-btn", 3, "click"], ["name", "crosshair", 3, "size"], ["title", "Toggle rulers", "aria-label", "Toggle rulers", 1, "tool-btn", 3, "click"], ["name", "move", 3, "size"], [1, "tool-btn", 3, "click", "title"], ["width", "11", "height", "11", "viewBox", "0 0 24 24", "fill", "#facc15", "aria-label", "Recommended", 1, "bg-star"], ["d", "M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01z"], [1, "tool-btn", 3, "click"]], template: function PrismCanvasToolbarComponent_Template(rf, ctx) { if (rf & 1) {
52
72
  i0.ɵɵelementStart(0, "div", 0)(1, "span", 1);
53
73
  i0.ɵɵtext(2, "Canvas");
54
74
  i0.ɵɵelementEnd();
55
75
  i0.ɵɵelementStart(3, "div", 2);
56
- i0.ɵɵrepeaterCreate(4, PrismCanvasToolbarComponent_For_5_Template, 2, 3, "button", 3, i0.ɵɵrepeaterTrackByIdentity);
76
+ i0.ɵɵrepeaterCreate(4, PrismCanvasToolbarComponent_For_5_Template, 3, 7, "button", 3, i0.ɵɵrepeaterTrackByIdentity);
57
77
  i0.ɵɵelementEnd();
58
78
  i0.ɵɵelement(6, "div", 4);
59
79
  i0.ɵɵelementStart(7, "span", 1);
60
80
  i0.ɵɵtext(8, "Zoom");
61
81
  i0.ɵɵelementEnd();
62
82
  i0.ɵɵelementStart(9, "div", 2);
63
- i0.ɵɵrepeaterCreate(10, PrismCanvasToolbarComponent_For_11_Template, 2, 3, "button", 3, _forTrack0);
83
+ i0.ɵɵrepeaterCreate(10, PrismCanvasToolbarComponent_For_11_Template, 2, 3, "button", 5, _forTrack0);
64
84
  i0.ɵɵelementEnd();
65
85
  i0.ɵɵelement(12, "div", 4);
66
- i0.ɵɵelementStart(13, "button", 5);
86
+ i0.ɵɵelementStart(13, "button", 6);
67
87
  i0.ɵɵlistener("click", function PrismCanvasToolbarComponent_Template_button_click_13_listener() { return ctx.canvas.toggleGuides(); });
68
- i0.ɵɵelement(14, "prism-icon", 6);
88
+ i0.ɵɵelement(14, "prism-icon", 7);
69
89
  i0.ɵɵtext(15, " Guides ");
70
90
  i0.ɵɵelementEnd();
71
- i0.ɵɵelementStart(16, "button", 7);
91
+ i0.ɵɵelementStart(16, "button", 8);
72
92
  i0.ɵɵlistener("click", function PrismCanvasToolbarComponent_Template_button_click_16_listener() { return ctx.canvas.toggleRulers(); });
73
- i0.ɵɵelement(17, "prism-icon", 8);
93
+ i0.ɵɵelement(17, "prism-icon", 9);
74
94
  i0.ɵɵtext(18, " Rulers ");
75
95
  i0.ɵɵelementEnd()();
76
96
  } if (rf & 2) {
@@ -86,7 +106,7 @@ export class PrismCanvasToolbarComponent {
86
106
  i0.ɵɵclassProp("tool-btn--active", ctx.canvas.rulers());
87
107
  i0.ɵɵadvance();
88
108
  i0.ɵɵproperty("size", 13);
89
- } }, dependencies: [PrismIconComponent], styles: [".canvas-toolbar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 8px 20px;\n border-bottom: 1px solid var(--prism-border);\n background: var(--prism-bg);\n min-height: 44px;\n }\n\n .tool-group[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 1px;\n padding: 2px;\n background: var(--prism-input-bg);\n border: 1px solid var(--prism-border);\n border-radius: var(--radius-md);\n }\n\n .tool-btn[_ngcontent-%COMP%] {\n height: 24px;\n padding: 0 9px;\n border-radius: var(--radius-sm);\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n transition: all var(--dur-fast);\n font-weight: 500;\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n }\n .tool-btn[_ngcontent-%COMP%]:hover {\n color: var(--prism-text);\n background: color-mix(in srgb, var(--prism-primary) 8%, transparent);\n }\n .tool-btn--active[_ngcontent-%COMP%] {\n color: var(--prism-text);\n background: color-mix(in srgb, var(--prism-primary) 15%, transparent);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--prism-primary) 30%, transparent);\n }\n\n .tool-sep[_ngcontent-%COMP%] {\n width: 1px;\n height: 18px;\n background: var(--prism-border);\n margin: 0 4px;\n }\n\n .tool-label[_ngcontent-%COMP%] {\n font-size: var(--fs-xs);\n color: var(--prism-text-ghost);\n text-transform: uppercase;\n letter-spacing: 0.08em;\n font-weight: 600;\n margin-right: 2px;\n }"], changeDetection: 0 });
109
+ } }, dependencies: [PrismIconComponent], styles: [".canvas-toolbar[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 8px 20px;\n border-bottom: 1px solid var(--prism-border);\n background: var(--prism-bg);\n min-height: 44px;\n }\n\n .tool-group[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 1px;\n padding: 2px;\n background: var(--prism-input-bg);\n border: 1px solid var(--prism-border);\n border-radius: var(--radius-md);\n }\n\n .tool-btn[_ngcontent-%COMP%] {\n height: 24px;\n padding: 0 9px;\n border-radius: var(--radius-sm);\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n transition: all var(--dur-fast);\n font-weight: 500;\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n }\n .tool-btn[_ngcontent-%COMP%]:hover {\n color: var(--prism-text);\n background: color-mix(in srgb, var(--prism-primary) 8%, transparent);\n }\n .tool-btn--active[_ngcontent-%COMP%] {\n color: var(--prism-text);\n background: color-mix(in srgb, var(--prism-primary) 15%, transparent);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--prism-primary) 30%, transparent);\n }\n .bg-star[_ngcontent-%COMP%] {\n flex-shrink: 0;\n filter: drop-shadow(0 0 2px rgba(250, 204, 21, 0.4));\n }\n\n .tool-sep[_ngcontent-%COMP%] {\n width: 1px;\n height: 18px;\n background: var(--prism-border);\n margin: 0 4px;\n }\n\n .tool-label[_ngcontent-%COMP%] {\n font-size: var(--fs-xs);\n color: var(--prism-text-ghost);\n text-transform: uppercase;\n letter-spacing: 0.08em;\n font-weight: 600;\n margin-right: 2px;\n }"], changeDetection: 0 });
90
110
  }
91
111
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PrismCanvasToolbarComponent, [{
92
112
  type: Component,
@@ -97,9 +117,29 @@ export class PrismCanvasToolbarComponent {
97
117
  @for (bg of bgs; track bg) {
98
118
  <button
99
119
  class="tool-btn"
100
- [class.tool-btn--active]="canvas.bg() === bg"
101
- (click)="canvas.setBg(bg)"
120
+ [class.tool-btn--active]="variantBg.effective() === bg"
121
+ [class.tool-btn--recommended]="variantBg.recommended() === bg"
122
+ [title]="
123
+ variantBg.recommended() === bg
124
+ ? 'Recommended background for this variant'
125
+ : null
126
+ "
127
+ (click)="setBg(bg)"
102
128
  >
129
+ @if (variantBg.recommended() === bg) {
130
+ <svg
131
+ class="bg-star"
132
+ width="11"
133
+ height="11"
134
+ viewBox="0 0 24 24"
135
+ fill="#facc15"
136
+ aria-label="Recommended"
137
+ >
138
+ <path
139
+ d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01z"
140
+ />
141
+ </svg>
142
+ }
103
143
  {{ capitalize(bg) }}
104
144
  </button>
105
145
  }
@@ -139,6 +179,6 @@ export class PrismCanvasToolbarComponent {
139
179
  Rulers
140
180
  </button>
141
181
  </div>
142
- `, styles: ["\n .canvas-toolbar {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 8px 20px;\n border-bottom: 1px solid var(--prism-border);\n background: var(--prism-bg);\n min-height: 44px;\n }\n\n .tool-group {\n display: flex;\n align-items: center;\n gap: 1px;\n padding: 2px;\n background: var(--prism-input-bg);\n border: 1px solid var(--prism-border);\n border-radius: var(--radius-md);\n }\n\n .tool-btn {\n height: 24px;\n padding: 0 9px;\n border-radius: var(--radius-sm);\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n transition: all var(--dur-fast);\n font-weight: 500;\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n }\n .tool-btn:hover {\n color: var(--prism-text);\n background: color-mix(in srgb, var(--prism-primary) 8%, transparent);\n }\n .tool-btn--active {\n color: var(--prism-text);\n background: color-mix(in srgb, var(--prism-primary) 15%, transparent);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--prism-primary) 30%, transparent);\n }\n\n .tool-sep {\n width: 1px;\n height: 18px;\n background: var(--prism-border);\n margin: 0 4px;\n }\n\n .tool-label {\n font-size: var(--fs-xs);\n color: var(--prism-text-ghost);\n text-transform: uppercase;\n letter-spacing: 0.08em;\n font-weight: 600;\n margin-right: 2px;\n }\n\n "] }]
182
+ `, styles: ["\n .canvas-toolbar {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 8px 20px;\n border-bottom: 1px solid var(--prism-border);\n background: var(--prism-bg);\n min-height: 44px;\n }\n\n .tool-group {\n display: flex;\n align-items: center;\n gap: 1px;\n padding: 2px;\n background: var(--prism-input-bg);\n border: 1px solid var(--prism-border);\n border-radius: var(--radius-md);\n }\n\n .tool-btn {\n height: 24px;\n padding: 0 9px;\n border-radius: var(--radius-sm);\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: var(--fs-sm);\n color: var(--prism-text-muted);\n transition: all var(--dur-fast);\n font-weight: 500;\n background: none;\n border: none;\n cursor: pointer;\n font-family: var(--font-sans);\n }\n .tool-btn:hover {\n color: var(--prism-text);\n background: color-mix(in srgb, var(--prism-primary) 8%, transparent);\n }\n .tool-btn--active {\n color: var(--prism-text);\n background: color-mix(in srgb, var(--prism-primary) 15%, transparent);\n box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--prism-primary) 30%, transparent);\n }\n .bg-star {\n flex-shrink: 0;\n filter: drop-shadow(0 0 2px rgba(250, 204, 21, 0.4));\n }\n\n .tool-sep {\n width: 1px;\n height: 18px;\n background: var(--prism-border);\n margin: 0 4px;\n }\n\n .tool-label {\n font-size: var(--fs-xs);\n color: var(--prism-text-ghost);\n text-transform: uppercase;\n letter-spacing: 0.08em;\n font-weight: 600;\n margin-right: 2px;\n }\n\n "] }]
143
183
  }], null, null); })();
144
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismCanvasToolbarComponent, { className: "PrismCanvasToolbarComponent", filePath: "app/canvas/prism-canvas-toolbar.component.ts", lineNumber: 128 }); })();
184
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismCanvasToolbarComponent, { className: "PrismCanvasToolbarComponent", filePath: "app/canvas/prism-canvas-toolbar.component.ts", lineNumber: 153 }); })();
@@ -1,7 +1,9 @@
1
+ import { PrismLayoutService } from '../services/prism-layout.service.js';
1
2
  import * as i0 from "@angular/core";
2
3
  export declare class PrismCodeDrawerComponent {
3
4
  private readonly navigationService;
4
5
  private readonly rendererService;
6
+ protected readonly layoutService: PrismLayoutService;
5
7
  protected readonly collapsed: import("@angular/core").WritableSignal<boolean>;
6
8
  protected readonly snippet: import("@angular/core").Signal<string>;
7
9
  protected readonly highlighted: import("@angular/core").Signal<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"prism-code-drawer.component.d.ts","sourceRoot":"","sources":["../../../src/app/canvas/prism-code-drawer.component.ts"],"names":[],"mappings":";AAyCA,qBAuGa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAkC;IACpE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAEhE,SAAS,CAAC,QAAQ,CAAC,SAAS,kDAAiB;IAE7C,SAAS,CAAC,QAAQ,CAAC,OAAO,yCAqBvB;IAEH,SAAS,CAAC,QAAQ,CAAC,WAAW,yCAI3B;IAEH,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI;yCAnCxB,wBAAwB;2CAAxB,wBAAwB;CAwCpC"}
1
+ {"version":3,"file":"prism-code-drawer.component.d.ts","sourceRoot":"","sources":["../../../src/app/canvas/prism-code-drawer.component.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;;AAkCzE,qBAuGa,wBAAwB;IACnC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAkC;IACpE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,SAAS,CAAC,QAAQ,CAAC,aAAa,qBAA8B;IAE9D,SAAS,CAAC,QAAQ,CAAC,SAAS,kDAA0C;IAEtE,SAAS,CAAC,QAAQ,CAAC,OAAO,yCAqBvB;IAEH,SAAS,CAAC,QAAQ,CAAC,WAAW,yCAI3B;IAEH,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI;yCApCxB,wBAAwB;2CAAxB,wBAAwB;CAyCpC"}
@@ -1,5 +1,6 @@
1
- import { Component, inject, computed, signal, ChangeDetectionStrategy, } from '@angular/core';
1
+ import { Component, inject, computed, ChangeDetectionStrategy, } from '@angular/core';
2
2
  import { PrismIconComponent } from '../icons/prism-icon.component.js';
3
+ import { PrismLayoutService } from '../services/prism-layout.service.js';
3
4
  import { PrismNavigationService } from '../services/prism-navigation.service.js';
4
5
  import { PrismRendererService } from '../services/prism-renderer.service.js';
5
6
  import { generateSnippet } from '../renderer/snippet-generator.js';
@@ -39,7 +40,8 @@ function tokenizeXml(code) {
39
40
  export class PrismCodeDrawerComponent {
40
41
  navigationService = inject(PrismNavigationService);
41
42
  rendererService = inject(PrismRendererService);
42
- collapsed = signal(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : []));
43
+ layoutService = inject(PrismLayoutService);
44
+ collapsed = this.layoutService.codeDrawerCollapsed;
43
45
  snippet = computed(() => {
44
46
  const comp = this.navigationService.activeComponent();
45
47
  if (!comp)
@@ -68,7 +70,7 @@ export class PrismCodeDrawerComponent {
68
70
  static ɵfac = function PrismCodeDrawerComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PrismCodeDrawerComponent)(); };
69
71
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PrismCodeDrawerComponent, selectors: [["prism-code-drawer"]], decls: 11, vars: 6, consts: [[1, "code-drawer"], [1, "code-head", 3, "click"], ["name", "chevron-down", 1, "chev", 3, "size"], ["name", "copy", 3, "size"], [1, "code-head-actions"], ["title", "Copy to clipboard", 1, "mini", 3, "click"], [1, "code-body", 3, "innerHTML"]], template: function PrismCodeDrawerComponent_Template(rf, ctx) { if (rf & 1) {
70
72
  i0.ɵɵelementStart(0, "div", 0)(1, "div", 1);
71
- i0.ɵɵlistener("click", function PrismCodeDrawerComponent_Template_div_click_1_listener() { return ctx.collapsed.update(v => !v); });
73
+ i0.ɵɵlistener("click", function PrismCodeDrawerComponent_Template_div_click_1_listener() { return ctx.layoutService.toggleCodeDrawer(); });
72
74
  i0.ɵɵelement(2, "prism-icon", 2)(3, "prism-icon", 3);
73
75
  i0.ɵɵelementStart(4, "span");
74
76
  i0.ɵɵtext(5, "Angular Template");
@@ -96,7 +98,7 @@ export class PrismCodeDrawerComponent {
96
98
  type: Component,
97
99
  args: [{ selector: 'prism-code-drawer', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [PrismIconComponent], template: `
98
100
  <div class="code-drawer" [class.is-collapsed]="collapsed()">
99
- <div class="code-head" (click)="collapsed.update(v => !v)">
101
+ <div class="code-head" (click)="layoutService.toggleCodeDrawer()">
100
102
  <prism-icon name="chevron-down" [size]="12" class="chev" />
101
103
  <prism-icon name="copy" [size]="12" />
102
104
  <span>Angular Template</span>
@@ -2,12 +2,14 @@ import { Injector, type Type } from '@angular/core';
2
2
  import { PrismNavigationService } from '../services/prism-navigation.service.js';
3
3
  import { PrismRendererService } from '../services/prism-renderer.service.js';
4
4
  import { PrismCanvasService } from '../services/prism-canvas.service.js';
5
+ import { PrismVariantBgService } from '../services/prism-variant-bg.service.js';
5
6
  import * as i0 from "@angular/core";
6
7
  export declare class PrismRendererComponent {
7
8
  protected readonly Math: Math;
8
9
  protected readonly navigationService: PrismNavigationService;
9
10
  protected readonly rendererService: PrismRendererService;
10
11
  protected readonly canvasService: PrismCanvasService;
12
+ protected readonly variantBg: PrismVariantBgService;
11
13
  private readonly eventLogService;
12
14
  private readonly manifestService;
13
15
  private readonly injector;
@@ -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;;AAGzE,qBAyHa,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,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;yCAvNJ,sBAAsB;2CAAtB,sBAAsB;CAqOlC"}
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,qBA8Ha,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"}
@@ -9,14 +9,16 @@ import { PrismPanelService } from '../services/prism-panel.service.js';
9
9
  import { PrismPluginService } from '../services/prism-plugin.service.js';
10
10
  import { PrismRendererService } from '../services/prism-renderer.service.js';
11
11
  import { PrismCanvasService } from '../services/prism-canvas.service.js';
12
+ import { PrismVariantBgService } from '../services/prism-variant-bg.service.js';
12
13
  import { PrismCanvasRulersComponent } from '../canvas/prism-canvas-rulers.component.js';
14
+ import { PrismCanvasBgPillComponent } from '../canvas/prism-canvas-bg-pill.component.js';
13
15
  import * as i0 from "@angular/core";
14
16
  const _c0 = ["outlet"];
15
- function PrismRendererComponent_Conditional_9_ng_container_0_Template(rf, ctx) { if (rf & 1) {
17
+ function PrismRendererComponent_Conditional_10_ng_container_0_Template(rf, ctx) { if (rf & 1) {
16
18
  i0.ɵɵelementContainer(0);
17
19
  } }
18
- function PrismRendererComponent_Conditional_9_Template(rf, ctx) { if (rf & 1) {
19
- i0.ɵɵtemplate(0, PrismRendererComponent_Conditional_9_ng_container_0_Template, 1, 0, "ng-container", 6);
20
+ function PrismRendererComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
21
+ i0.ɵɵtemplate(0, PrismRendererComponent_Conditional_10_ng_container_0_Template, 1, 0, "ng-container", 6);
20
22
  } if (rf & 2) {
21
23
  const ctx_r0 = i0.ɵɵnextContext();
22
24
  i0.ɵɵproperty("ngComponentOutlet", ctx_r0.activeOverlay())("ngComponentOutletInputs", ctx_r0.overlayInputs)("ngComponentOutletInjector", ctx_r0.overlayInjector());
@@ -26,6 +28,7 @@ export class PrismRendererComponent {
26
28
  navigationService = inject(PrismNavigationService);
27
29
  rendererService = inject(PrismRendererService);
28
30
  canvasService = inject(PrismCanvasService);
31
+ variantBg = inject(PrismVariantBgService);
29
32
  eventLogService = inject(PrismEventLogService);
30
33
  manifestService = inject(PrismManifestService);
31
34
  injector = inject(Injector);
@@ -204,31 +207,37 @@ export class PrismRendererComponent {
204
207
  i0.ɵɵviewQuerySignal(ctx.outlet, _c0, 5, ViewContainerRef);
205
208
  } if (rf & 2) {
206
209
  i0.ɵɵqueryAdvance();
207
- } }, decls: 10, vars: 7, 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) {
210
+ } }, decls: 11, vars: 7, 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) {
208
211
  i0.ɵɵelementStart(0, "div", 1)(1, "div", 2)(2, "span", 3);
209
212
  i0.ɵɵtext(3);
210
213
  i0.ɵɵelementEnd()();
211
- i0.ɵɵelement(4, "div", 4)(5, "prism-canvas-rulers");
212
- i0.ɵɵelementStart(6, "div", 5);
213
- i0.ɵɵelementContainer(7, null, 0);
214
- i0.ɵɵconditionalCreate(9, PrismRendererComponent_Conditional_9_Template, 1, 3, "ng-container");
214
+ i0.ɵɵelement(4, "div", 4)(5, "prism-canvas-rulers")(6, "prism-canvas-bg-pill");
215
+ i0.ɵɵelementStart(7, "div", 5);
216
+ i0.ɵɵelementContainer(8, null, 0);
217
+ i0.ɵɵconditionalCreate(10, PrismRendererComponent_Conditional_10_Template, 1, 3, "ng-container");
215
218
  i0.ɵɵelementEnd()();
216
219
  } if (rf & 2) {
217
- i0.ɵɵattribute("data-bg", ctx.canvasService.bg());
220
+ i0.ɵɵattribute("data-bg", ctx.variantBg.effective());
218
221
  i0.ɵɵadvance(3);
219
222
  i0.ɵɵtextInterpolate1("", ctx.Math.round(ctx.canvasService.zoom() * 100), "%");
220
223
  i0.ɵɵadvance();
221
224
  i0.ɵɵclassProp("visible", ctx.canvasService.guides());
222
- i0.ɵɵadvance(2);
225
+ i0.ɵɵadvance(3);
223
226
  i0.ɵɵstyleProp("--zoom", ctx.canvasService.zoom());
224
227
  i0.ɵɵadvance(3);
225
- i0.ɵɵconditional(ctx.activeOverlay() ? 9 : -1);
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 max-width: 600px;\n }"], changeDetection: 0 });
228
+ i0.ɵɵconditional(ctx.activeOverlay() ? 10 : -1);
229
+ } }, dependencies: [PrismCanvasRulersComponent,
230
+ PrismCanvasBgPillComponent,
231
+ 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 max-width: 600px;\n }"], changeDetection: 0 });
227
232
  }
228
233
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PrismRendererComponent, [{
229
234
  type: Component,
230
- args: [{ selector: 'prism-renderer', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [PrismCanvasRulersComponent, NgComponentOutlet], template: `
231
- <div class="prism-canvas-stage" [attr.data-bg]="canvasService.bg()">
235
+ args: [{ selector: 'prism-renderer', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
236
+ PrismCanvasRulersComponent,
237
+ PrismCanvasBgPillComponent,
238
+ NgComponentOutlet,
239
+ ], template: `
240
+ <div class="prism-canvas-stage" [attr.data-bg]="variantBg.effective()">
232
241
  <div class="canvas-badges">
233
242
  <span class="c-badge"
234
243
  >{{ Math.round(canvasService.zoom() * 100) }}%</span
@@ -239,6 +248,7 @@ export class PrismRendererComponent {
239
248
  [class.visible]="canvasService.guides()"
240
249
  ></div>
241
250
  <prism-canvas-rulers />
251
+ <prism-canvas-bg-pill />
242
252
 
243
253
  <div class="demo-wrap" [style.--zoom]="canvasService.zoom()">
244
254
  <ng-container #outlet />
@@ -257,7 +267,7 @@ export class PrismRendererComponent {
257
267
  }], () => [], { outlet: [{ type: i0.ViewChild, args: ['outlet', { ...{
258
268
  read: ViewContainerRef,
259
269
  }, isSignal: true }] }] }); })();
260
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismRendererComponent, { className: "PrismRendererComponent", filePath: "app/renderer/prism-renderer.component.ts", lineNumber: 156 }); })();
270
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismRendererComponent, { className: "PrismRendererComponent", filePath: "app/renderer/prism-renderer.component.ts", lineNumber: 163 }); })();
261
271
  function parseContentToNodes(content) {
262
272
  if (typeof content === 'string') {
263
273
  return [htmlToNodes(content)];
@@ -1,5 +1,6 @@
1
+ import { type CanvasBg } from '../../shared/canvas-bg.type.js';
1
2
  import * as i0 from "@angular/core";
2
- export type CanvasBg = 'dots' | 'plain' | 'light' | 'dark' | 'checker';
3
+ export type { CanvasBg };
3
4
  export declare class PrismCanvasService {
4
5
  readonly bg: import("@angular/core").WritableSignal<CanvasBg>;
5
6
  readonly zoom: import("@angular/core").WritableSignal<number>;
@@ -1 +1 @@
1
- {"version":3,"file":"prism-canvas.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-canvas.service.ts"],"names":[],"mappings":";AAEA,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAIvE,qBACa,kBAAkB;IAC7B,QAAQ,CAAC,EAAE,mDAA4B;IACvC,QAAQ,CAAC,IAAI,iDAAa;IAC1B,QAAQ,CAAC,MAAM,kDAAiB;IAChC,QAAQ,CAAC,MAAM,kDAAiB;;IAMhC,KAAK,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI;IAKzB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAKxB,YAAY,IAAI,IAAI;IAKpB,YAAY,IAAI,IAAI;IAKpB,OAAO,CAAC,eAAe;IAkBvB,OAAO,CAAC,IAAI;yCAhDD,kBAAkB;6CAAlB,kBAAkB;CA6D9B"}
1
+ {"version":3,"file":"prism-canvas.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-canvas.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,gCAAgC,CAAC;;AAE3E,YAAY,EAAE,QAAQ,EAAE,CAAC;AAIzB,qBACa,kBAAkB;IAC7B,QAAQ,CAAC,EAAE,mDAA4B;IACvC,QAAQ,CAAC,IAAI,iDAAa;IAC1B,QAAQ,CAAC,MAAM,kDAAiB;IAChC,QAAQ,CAAC,MAAM,kDAAiB;;IAMhC,KAAK,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI;IAKzB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI;IAKxB,YAAY,IAAI,IAAI;IAKpB,YAAY,IAAI,IAAI;IAKpB,OAAO,CAAC,eAAe;IAcvB,OAAO,CAAC,IAAI;yCA5CD,kBAAkB;6CAAlB,kBAAkB;CAyD9B"}
@@ -1,4 +1,5 @@
1
1
  import { Injectable, signal } from '@angular/core';
2
+ import { CANVAS_BGS } from '../../shared/canvas-bg.type.js';
2
3
  import * as i0 from "@angular/core";
3
4
  const STORAGE_KEY = 'ng-prism-canvas';
4
5
  export class PrismCanvasService {
@@ -31,7 +32,7 @@ export class PrismCanvasService {
31
32
  if (!raw)
32
33
  return;
33
34
  const d = JSON.parse(raw);
34
- if (['dots', 'plain', 'light', 'dark', 'checker'].includes(d['bg'])) {
35
+ if (CANVAS_BGS.includes(d['bg'])) {
35
36
  this.bg.set(d['bg']);
36
37
  }
37
38
  if (typeof d['zoom'] === 'number')
@@ -7,11 +7,13 @@ export declare class PrismLayoutService {
7
7
  readonly sidebarWidth: import("@angular/core").WritableSignal<number>;
8
8
  readonly panelHeight: import("@angular/core").WritableSignal<number>;
9
9
  readonly panelWidth: import("@angular/core").WritableSignal<number>;
10
+ readonly codeDrawerCollapsed: import("@angular/core").WritableSignal<boolean>;
10
11
  constructor();
11
12
  toggleSidebar(): void;
12
13
  toggleAddons(): void;
13
14
  toggleToolbar(): void;
14
15
  toggleOrientation(): void;
16
+ toggleCodeDrawer(): void;
15
17
  setSidebarWidth(px: number): void;
16
18
  setPanelHeight(px: number): void;
17
19
  setPanelWidth(px: number): void;
@@ -1 +1 @@
1
- {"version":3,"file":"prism-layout.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-layout.service.ts"],"names":[],"mappings":";AAIA,qBACa,kBAAkB;IAC7B,QAAQ,CAAC,cAAc,kDAAgB;IACvC,QAAQ,CAAC,aAAa,kDAAgB;IACtC,QAAQ,CAAC,cAAc,kDAAgB;IACvC,QAAQ,CAAC,iBAAiB,6DAAwC;IAClE,QAAQ,CAAC,YAAY,iDAAe;IACpC,QAAQ,CAAC,WAAW,iDAAe;IACnC,QAAQ,CAAC,UAAU,iDAAe;;IAMlC,aAAa,IAAI,IAAI;IAKrB,YAAY,IAAI,IAAI;IAKpB,aAAa,IAAI,IAAI;IAKrB,iBAAiB,IAAI,IAAI;IAKzB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKjC,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKhC,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAK/B,OAAO,CAAC,eAAe;IAiBvB,OAAO,CAAC,aAAa;yCAjEV,kBAAkB;6CAAlB,kBAAkB;CA6E9B"}
1
+ {"version":3,"file":"prism-layout.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-layout.service.ts"],"names":[],"mappings":";AAIA,qBACa,kBAAkB;IAC7B,QAAQ,CAAC,cAAc,kDAAgB;IACvC,QAAQ,CAAC,aAAa,kDAAgB;IACtC,QAAQ,CAAC,cAAc,kDAAgB;IACvC,QAAQ,CAAC,iBAAiB,6DAAwC;IAClE,QAAQ,CAAC,YAAY,iDAAe;IACpC,QAAQ,CAAC,WAAW,iDAAe;IACnC,QAAQ,CAAC,UAAU,iDAAe;IAClC,QAAQ,CAAC,mBAAmB,kDAAiB;;IAM7C,aAAa,IAAI,IAAI;IAKrB,YAAY,IAAI,IAAI;IAKpB,aAAa,IAAI,IAAI;IAKrB,iBAAiB,IAAI,IAAI;IAKzB,gBAAgB,IAAI,IAAI;IAKxB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKjC,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAKhC,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAK/B,OAAO,CAAC,eAAe;IA4BvB,OAAO,CAAC,aAAa;yCAlFV,kBAAkB;6CAAlB,kBAAkB;CA+F9B"}
@@ -9,23 +9,28 @@ export class PrismLayoutService {
9
9
  sidebarWidth = signal(280, ...(ngDevMode ? [{ debugName: "sidebarWidth" }] : []));
10
10
  panelHeight = signal(300, ...(ngDevMode ? [{ debugName: "panelHeight" }] : []));
11
11
  panelWidth = signal(320, ...(ngDevMode ? [{ debugName: "panelWidth" }] : []));
12
+ codeDrawerCollapsed = signal(false, ...(ngDevMode ? [{ debugName: "codeDrawerCollapsed" }] : []));
12
13
  constructor() {
13
14
  this.loadFromStorage();
14
15
  }
15
16
  toggleSidebar() {
16
- this.sidebarVisible.update(v => !v);
17
+ this.sidebarVisible.update((v) => !v);
17
18
  this.saveToStorage();
18
19
  }
19
20
  toggleAddons() {
20
- this.addonsVisible.update(v => !v);
21
+ this.addonsVisible.update((v) => !v);
21
22
  this.saveToStorage();
22
23
  }
23
24
  toggleToolbar() {
24
- this.toolbarVisible.update(v => !v);
25
+ this.toolbarVisible.update((v) => !v);
25
26
  this.saveToStorage();
26
27
  }
27
28
  toggleOrientation() {
28
- this.addonsOrientation.update(v => (v === 'bottom' ? 'right' : 'bottom'));
29
+ this.addonsOrientation.update((v) => (v === 'bottom' ? 'right' : 'bottom'));
30
+ this.saveToStorage();
31
+ }
32
+ toggleCodeDrawer() {
33
+ this.codeDrawerCollapsed.update((v) => !v);
29
34
  this.saveToStorage();
30
35
  }
31
36
  setSidebarWidth(px) {
@@ -52,7 +57,8 @@ export class PrismLayoutService {
52
57
  this.addonsVisible.set(d['addonsVisible']);
53
58
  if (typeof d['toolbarVisible'] === 'boolean')
54
59
  this.toolbarVisible.set(d['toolbarVisible']);
55
- if (d['addonsOrientation'] === 'bottom' || d['addonsOrientation'] === 'right') {
60
+ if (d['addonsOrientation'] === 'bottom' ||
61
+ d['addonsOrientation'] === 'right') {
56
62
  this.addonsOrientation.set(d['addonsOrientation']);
57
63
  }
58
64
  if (typeof d['sidebarWidth'] === 'number')
@@ -61,6 +67,8 @@ export class PrismLayoutService {
61
67
  this.panelHeight.set(Math.max(100, Math.min(600, d['panelHeight'])));
62
68
  if (typeof d['panelWidth'] === 'number')
63
69
  this.panelWidth.set(Math.max(200, Math.min(600, d['panelWidth'])));
70
+ if (typeof d['codeDrawerCollapsed'] === 'boolean')
71
+ this.codeDrawerCollapsed.set(d['codeDrawerCollapsed']);
64
72
  }
65
73
  catch { }
66
74
  }
@@ -73,6 +81,7 @@ export class PrismLayoutService {
73
81
  sidebarWidth: this.sidebarWidth(),
74
82
  panelHeight: this.panelHeight(),
75
83
  panelWidth: this.panelWidth(),
84
+ codeDrawerCollapsed: this.codeDrawerCollapsed(),
76
85
  };
77
86
  localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
78
87
  }
@@ -1,6 +1,6 @@
1
1
  import * as i0 from "@angular/core";
2
2
  export declare class PrismThemeService {
3
- readonly theme: import("@angular/core").WritableSignal<"dark" | "light">;
3
+ readonly theme: import("@angular/core").WritableSignal<"light" | "dark">;
4
4
  readonly isDark: import("@angular/core").Signal<boolean>;
5
5
  private configOverrides;
6
6
  private darkOverrides;
@@ -0,0 +1,19 @@
1
+ import type { CanvasBg } from '../../shared/canvas-bg.type.js';
2
+ import * as i0 from "@angular/core";
3
+ export declare class PrismVariantBgService {
4
+ private readonly canvas;
5
+ private readonly navigation;
6
+ private readonly renderer;
7
+ readonly recommended: import("@angular/core").Signal<CanvasBg | null>;
8
+ private readonly _override;
9
+ readonly override: import("@angular/core").Signal<CanvasBg | null>;
10
+ readonly effective: import("@angular/core").Signal<CanvasBg>;
11
+ /** True when the user's override differs from the (non-null) recommended bg. */
12
+ readonly isDeviating: import("@angular/core").Signal<boolean>;
13
+ constructor();
14
+ setOverride(bg: CanvasBg): void;
15
+ clearOverride(): void;
16
+ static ɵfac: i0.ɵɵFactoryDeclaration<PrismVariantBgService, never>;
17
+ static ɵprov: i0.ɵɵInjectableDeclaration<PrismVariantBgService>;
18
+ }
19
+ //# sourceMappingURL=prism-variant-bg.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prism-variant-bg.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-variant-bg.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;;AAK/D,qBACa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IACrD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;IAC7D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgC;IAEzD,QAAQ,CAAC,WAAW,kDAMjB;IAEH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;IAC3D,QAAQ,CAAC,QAAQ,kDAA+B;IAEhD,QAAQ,CAAC,SAAS,2CAEhB;IAEF,gFAAgF;IAChF,QAAQ,CAAC,WAAW,0CAMjB;;IAUH,WAAW,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI;IAI/B,aAAa,IAAI,IAAI;yCAzCV,qBAAqB;6CAArB,qBAAqB;CA4CjC"}
@@ -0,0 +1,45 @@
1
+ import { computed, effect, inject, Injectable, signal } from '@angular/core';
2
+ import { PrismCanvasService } from './prism-canvas.service.js';
3
+ import { PrismNavigationService } from './prism-navigation.service.js';
4
+ import { PrismRendererService } from './prism-renderer.service.js';
5
+ import * as i0 from "@angular/core";
6
+ export class PrismVariantBgService {
7
+ canvas = inject(PrismCanvasService);
8
+ navigation = inject(PrismNavigationService);
9
+ renderer = inject(PrismRendererService);
10
+ recommended = computed(() => {
11
+ const comp = this.navigation.activeComponent();
12
+ if (!comp)
13
+ return null;
14
+ const variant = comp.meta.showcaseConfig.variants?.[this.renderer.activeVariantIndex()];
15
+ return variant?.bg ?? comp.meta.showcaseConfig.bg ?? null;
16
+ }, ...(ngDevMode ? [{ debugName: "recommended" }] : []));
17
+ _override = signal(null, ...(ngDevMode ? [{ debugName: "_override" }] : []));
18
+ override = this._override.asReadonly();
19
+ effective = computed(() => this._override() ?? this.recommended() ?? this.canvas.bg(), ...(ngDevMode ? [{ debugName: "effective" }] : []));
20
+ /** True when the user's override differs from the (non-null) recommended bg. */
21
+ isDeviating = computed(() => {
22
+ const override = this._override();
23
+ const recommended = this.recommended();
24
+ return (override !== null && recommended !== null && override !== recommended);
25
+ }, ...(ngDevMode ? [{ debugName: "isDeviating" }] : []));
26
+ constructor() {
27
+ effect(() => {
28
+ this.navigation.activeComponent();
29
+ this.renderer.activeVariantIndex();
30
+ this._override.set(null);
31
+ });
32
+ }
33
+ setOverride(bg) {
34
+ this._override.set(bg);
35
+ }
36
+ clearOverride() {
37
+ this._override.set(null);
38
+ }
39
+ static ɵfac = function PrismVariantBgService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PrismVariantBgService)(); };
40
+ static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: PrismVariantBgService, factory: PrismVariantBgService.ɵfac, providedIn: 'root' });
41
+ }
42
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PrismVariantBgService, [{
43
+ type: Injectable,
44
+ args: [{ providedIn: 'root' }]
45
+ }], () => [], null); })();
@@ -1 +1 @@
1
- {"version":3,"file":"component.scanner.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/component.scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAKrE;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE,EACpB,OAAO,EAAE,EAAE,CAAC,WAAW,GACtB,gBAAgB,EAAE,CA0CpB"}
1
+ {"version":3,"file":"component.scanner.d.ts","sourceRoot":"","sources":["../../../src/builder/scanner/component.scanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAiBrE;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,EAAE,CAAC,MAAM,EAAE,EACpB,OAAO,EAAE,EAAE,CAAC,WAAW,GACtB,gBAAgB,EAAE,CAyCpB"}
@@ -1,15 +1,18 @@
1
1
  import ts from 'typescript';
2
- import { evaluateExpression, findDecorator, getDecoratorArgument } from './ast-utils.js';
2
+ import { CANVAS_BGS } from '../../shared/canvas-bg.type.js';
3
+ import { evaluateExpression, findDecorator, getDecoratorArgument, } from './ast-utils.js';
3
4
  import { extractInputs, extractOutputs } from './input.extractor.js';
5
+ function isCanvasBg(value) {
6
+ return (typeof value === 'string' &&
7
+ CANVAS_BGS.includes(value));
8
+ }
4
9
  /**
5
10
  * Scan exported symbols for Angular components annotated with @Showcase.
6
11
  */
7
12
  export function scanComponents(exports, checker) {
8
13
  const components = [];
9
14
  for (const sym of exports) {
10
- const resolved = sym.flags & ts.SymbolFlags.Alias
11
- ? checker.getAliasedSymbol(sym)
12
- : sym;
15
+ const resolved = sym.flags & ts.SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym;
13
16
  const classDecl = resolved.declarations?.find(ts.isClassDeclaration);
14
17
  if (!classDecl)
15
18
  continue;
@@ -76,8 +79,25 @@ function extractShowcaseConfig(decorator, className) {
76
79
  config.host = obj['host'];
77
80
  if (obj['renderPage'])
78
81
  config.renderPage = obj['renderPage'];
82
+ if (obj['bg'] !== undefined) {
83
+ if (isCanvasBg(obj['bg'])) {
84
+ config.bg = obj['bg'];
85
+ }
86
+ else {
87
+ console.warn(`⚠ ng-prism: ${className} declares invalid bg "${String(obj['bg'])}" — ` +
88
+ `expected one of: dots, plain, light, dark, checker. Skipping.`);
89
+ }
90
+ }
79
91
  if (Array.isArray(obj['variants'])) {
80
- config.variants = obj['variants'];
92
+ config.variants = obj['variants'].map((variant) => {
93
+ const cleaned = { ...variant };
94
+ if (variant['bg'] !== undefined && !isCanvasBg(variant['bg'])) {
95
+ console.warn(`⚠ ng-prism: ${className} variant "${String(variant['name'])}" declares ` +
96
+ `invalid bg "${String(variant['bg'])}" — expected one of: dots, plain, light, dark, checker. Skipping.`);
97
+ delete cleaned['bg'];
98
+ }
99
+ return cleaned;
100
+ });
81
101
  }
82
102
  return config;
83
103
  }
@@ -1,4 +1,5 @@
1
1
  import type { Provider } from '@angular/core';
2
+ import type { CanvasBg } from '../shared/canvas-bg.type.js';
2
3
  export interface DirectiveHost {
3
4
  selector: string;
4
5
  import: {
@@ -30,6 +31,13 @@ export interface ShowcaseConfig {
30
31
  providers?: Provider[];
31
32
  /** Arbitrary metadata for plugins (e.g. { figma: 'https://...' }) */
32
33
  meta?: Record<string, unknown>;
34
+ /**
35
+ * Recommended canvas background for this component. Applied when the user
36
+ * opens the component, unless a variant defines its own `bg`. The user
37
+ * can override via the canvas toolbar; the override is transient and
38
+ * resets when switching variants or components.
39
+ */
40
+ bg?: CanvasBg;
33
41
  /** Host element for directive showcases. String = HTML element (e.g. '<button class="btn">'), object = Angular component. */
34
42
  host?: string | DirectiveHost;
35
43
  /** Title of a registered ComponentPage to render instead of the component itself. Use for complex components that need template projections or mock data. The page component can inject PrismRendererService to react to control panel changes. */
@@ -50,5 +58,7 @@ export interface Variant {
50
58
  description?: string;
51
59
  /** Arbitrary metadata for plugins (e.g. { figma: 'https://...?node-id=12-34' }) */
52
60
  meta?: Record<string, unknown>;
61
+ /** Recommended canvas background for this variant. Overrides `ShowcaseConfig.bg`. */
62
+ bg?: CanvasBg;
53
63
  }
54
64
  //# sourceMappingURL=showcase.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"showcase.types.d.ts","sourceRoot":"","sources":["../../src/decorator/showcase.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2IAA2I;IAC3I,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iJAAiJ;IACjJ,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,oCAAoC;IACpC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;;;;OAIG;IACH,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,6HAA6H;IAC7H,IAAI,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC;IAC9B,mPAAmP;IACnP,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,OAAO;IACtB,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC"}
1
+ {"version":3,"file":"showcase.types.d.ts","sourceRoot":"","sources":["../../src/decorator/showcase.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAE5D,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,cAAc;IAC7B,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2IAA2I;IAC3I,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iJAAiJ;IACjJ,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,oCAAoC;IACpC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;;;;OAIG;IACH,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B;;;;;OAKG;IACH,EAAE,CAAC,EAAE,QAAQ,CAAC;IACd,6HAA6H;IAC7H,IAAI,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC;IAC9B,mPAAmP;IACnP,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,OAAO;IACtB,gBAAgB;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,4CAA4C;IAC5C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,qFAAqF;IACrF,EAAE,CAAC,EAAE,QAAQ,CAAC;CACf"}
@@ -0,0 +1,3 @@
1
+ export type CanvasBg = 'dots' | 'plain' | 'light' | 'dark' | 'checker';
2
+ export declare const CANVAS_BGS: readonly CanvasBg[];
3
+ //# sourceMappingURL=canvas-bg.type.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvas-bg.type.d.ts","sourceRoot":"","sources":["../../src/shared/canvas-bg.type.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;AAEvE,eAAO,MAAM,UAAU,EAAE,SAAS,QAAQ,EAMhC,CAAC"}
@@ -0,0 +1,7 @@
1
+ export const CANVAS_BGS = [
2
+ 'dots',
3
+ 'plain',
4
+ 'light',
5
+ 'dark',
6
+ 'checker',
7
+ ];
@@ -1 +1 @@
1
- {"version":3,"file":"showcase-strip.transformer.d.ts","sourceRoot":"","sources":["../../src/transformer/showcase-strip.transformer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAmK5B,wBAAgB,8BAA8B,IAAI,EAAE,CAAC,kBAAkB,CAAC,EAAE,CAAC,UAAU,CAAC,CAuCrF;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,SAAY,GAAG,MAAM,CAWpF"}
1
+ {"version":3,"file":"showcase-strip.transformer.d.ts","sourceRoot":"","sources":["../../src/transformer/showcase-strip.transformer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAC;AAoN5B,wBAAgB,8BAA8B,IAAI,EAAE,CAAC,kBAAkB,CAAC,EAAE,CAAC,UAAU,CAAC,CA8CrF;AA6BD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,MAAM,EACd,QAAQ,SAAY,GACnB,MAAM,CAgCR"}
@@ -4,9 +4,9 @@ function isShowcaseModule(specifier) {
4
4
  return SHOWCASE_MODULE_PREFIXES.some((prefix) => specifier === prefix || specifier.startsWith(prefix + '/'));
5
5
  }
6
6
  function isShowcaseCall(expr, names) {
7
- return ts.isCallExpression(expr)
8
- && ts.isIdentifier(expr.expression)
9
- && names.has(expr.expression.text);
7
+ return (ts.isCallExpression(expr) &&
8
+ ts.isIdentifier(expr.expression) &&
9
+ names.has(expr.expression.text));
10
10
  }
11
11
  function isLoweredShowcaseCall(expr, names) {
12
12
  if (!ts.isCallExpression(expr))
@@ -15,11 +15,21 @@ function isLoweredShowcaseCall(expr, names) {
15
15
  }
16
16
  function tryHandleDecorateStatement(stmt, names, factory) {
17
17
  const expr = stmt.expression;
18
- if (!ts.isBinaryExpression(expr) || expr.operatorToken.kind !== ts.SyntaxKind.EqualsToken) {
18
+ if (!ts.isBinaryExpression(expr) ||
19
+ expr.operatorToken.kind !== ts.SyntaxKind.EqualsToken) {
19
20
  return undefined;
20
21
  }
21
- const rhs = expr.right;
22
- if (!ts.isCallExpression(rhs) || !ts.isIdentifier(rhs.expression) || rhs.expression.text !== '__decorate') {
22
+ const chain = [];
23
+ let current = expr;
24
+ while (ts.isBinaryExpression(current) &&
25
+ current.operatorToken.kind === ts.SyntaxKind.EqualsToken) {
26
+ chain.push(current);
27
+ current = current.right;
28
+ }
29
+ const rhs = current;
30
+ if (!ts.isCallExpression(rhs) ||
31
+ !ts.isIdentifier(rhs.expression) ||
32
+ rhs.expression.text !== '__decorate') {
23
33
  return undefined;
24
34
  }
25
35
  const args = rhs.arguments;
@@ -36,14 +46,18 @@ function tryHandleDecorateStatement(stmt, names, factory) {
36
46
  }
37
47
  const newArray = factory.updateArrayLiteralExpression(decoratorArray, remaining);
38
48
  const newCall = factory.updateCallExpression(rhs, rhs.expression, rhs.typeArguments, [newArray, ...args.slice(1)]);
39
- const newBinary = factory.updateBinaryExpression(expr, expr.left, expr.operatorToken, newCall);
40
- const newStmt = factory.updateExpressionStatement(stmt, newBinary);
49
+ let newRhs = newCall;
50
+ for (let i = chain.length - 1; i >= 0; i--) {
51
+ const link = chain[i];
52
+ newRhs = factory.updateBinaryExpression(link, link.left, link.operatorToken, newRhs);
53
+ }
54
+ const newStmt = factory.updateExpressionStatement(stmt, newRhs);
41
55
  return { action: 'replace', statement: newStmt };
42
56
  }
43
57
  function isShowcaseDecorator(d, names) {
44
- return ts.isCallExpression(d.expression)
45
- && ts.isIdentifier(d.expression.expression)
46
- && names.has(d.expression.expression.text);
58
+ return (ts.isCallExpression(d.expression) &&
59
+ ts.isIdentifier(d.expression.expression) &&
60
+ names.has(d.expression.expression.text));
47
61
  }
48
62
  function stripNativeDecorators(node, names, factory) {
49
63
  const decorators = ts.getDecorators(node);
@@ -114,7 +128,8 @@ export function createShowcaseStripTransformer() {
114
128
  continue;
115
129
  const decorateResult = tryHandleDecorateStatement(stmt, names, factory);
116
130
  if (decorateResult) {
117
- if (decorateResult.action === 'replace' && decorateResult.statement) {
131
+ if (decorateResult.action === 'replace' &&
132
+ decorateResult.statement) {
118
133
  filtered.push(decorateResult.statement);
119
134
  }
120
135
  continue;
@@ -134,6 +149,21 @@ export function createShowcaseStripTransformer() {
134
149
  };
135
150
  };
136
151
  }
152
+ function findShowcaseLeak(sourceFile, names) {
153
+ let leak;
154
+ function visit(node) {
155
+ if (leak)
156
+ return;
157
+ if (ts.isIdentifier(node) && names.has(node.text)) {
158
+ const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
159
+ leak = { name: node.text, line: line + 1 };
160
+ return;
161
+ }
162
+ ts.forEachChild(node, visit);
163
+ }
164
+ visit(sourceFile);
165
+ return leak;
166
+ }
137
167
  export function stripShowcaseDecorators(source, fileName = 'file.ts') {
138
168
  const sourceFile = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true);
139
169
  const importInfo = findShowcaseImports(sourceFile);
@@ -143,5 +173,12 @@ export function stripShowcaseDecorators(source, fileName = 'file.ts') {
143
173
  const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
144
174
  const output = printer.printFile(result.transformed[0]);
145
175
  result.dispose();
176
+ const audited = ts.createSourceFile(fileName, output, ts.ScriptTarget.Latest, true);
177
+ const leak = findShowcaseLeak(audited, importInfo.localNames);
178
+ if (leak) {
179
+ throw new Error(`[ng-prism] Showcase reference '${leak.name}' left in output after stripping at ${fileName}:${leak.line}. ` +
180
+ `The Showcase import was removed but a reference remains — this would cause a ReferenceError at runtime. ` +
181
+ `This is a transformer bug; please report the input source.`);
182
+ }
146
183
  return output;
147
184
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-prism/core",
3
- "version": "21.9.1",
3
+ "version": "21.10.0",
4
4
  "description": "ng-prism — the Angular-native Storybook alternative. Lightweight component showcase with @Showcase decorator, no story files needed.",
5
5
  "keywords": [
6
6
  "ng-prism",