@ng-prism/core 22.1.3 → 22.2.0-beta.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 (39) hide show
  1. package/dist/app/discovery-manifest.d.ts +13 -0
  2. package/dist/app/discovery-manifest.d.ts.map +1 -0
  3. package/dist/app/discovery-manifest.js +106 -0
  4. package/dist/app/icons/prism-icon.component.d.ts +15 -0
  5. package/dist/app/icons/prism-icon.component.d.ts.map +1 -1
  6. package/dist/app/icons/prism-icon.component.js +26 -2
  7. package/dist/app/provide-prism.d.ts.map +1 -1
  8. package/dist/app/provide-prism.js +7 -12
  9. package/dist/app/renderer/overlay-resolver.d.ts +25 -0
  10. package/dist/app/renderer/overlay-resolver.d.ts.map +1 -0
  11. package/dist/app/renderer/overlay-resolver.js +25 -0
  12. package/dist/app/renderer/prism-renderer.component.d.ts +2 -0
  13. package/dist/app/renderer/prism-renderer.component.d.ts.map +1 -1
  14. package/dist/app/renderer/prism-renderer.component.js +46 -41
  15. package/dist/app/renderer/snippet-generator.d.ts.map +1 -1
  16. package/dist/app/renderer/snippet-generator.js +13 -5
  17. package/dist/app/services/prism-canvas.service.d.ts +1 -0
  18. package/dist/app/services/prism-canvas.service.d.ts.map +1 -1
  19. package/dist/app/services/prism-canvas.service.js +11 -1
  20. package/dist/app/services/prism-capture.service.d.ts +42 -0
  21. package/dist/app/services/prism-capture.service.d.ts.map +1 -0
  22. package/dist/app/services/prism-capture.service.js +100 -0
  23. package/dist/app/services/prism-persistence.service.d.ts +1 -0
  24. package/dist/app/services/prism-persistence.service.d.ts.map +1 -1
  25. package/dist/app/services/prism-persistence.service.js +17 -5
  26. package/dist/app/services/prism-url-state.service.d.ts.map +1 -1
  27. package/dist/app/services/prism-url-state.service.js +7 -2
  28. package/dist/app/services/prism-variant-bg.service.d.ts +28 -0
  29. package/dist/app/services/prism-variant-bg.service.d.ts.map +1 -1
  30. package/dist/app/services/prism-variant-bg.service.js +34 -3
  31. package/dist/plugin/index.d.ts +5 -1
  32. package/dist/plugin/index.d.ts.map +1 -1
  33. package/dist/plugin/index.js +2 -0
  34. package/dist/plugin/plugin.types.d.ts +40 -0
  35. package/dist/plugin/plugin.types.d.ts.map +1 -1
  36. package/dist/shared/variant-bg.d.ts +53 -0
  37. package/dist/shared/variant-bg.d.ts.map +1 -0
  38. package/dist/shared/variant-bg.js +43 -0
  39. package/package.json +1 -1
@@ -0,0 +1,13 @@
1
+ import type { DiscoveryManifest, RuntimeManifest } from '../plugin/plugin.types.js';
2
+ /** Sanitised `meta`, or `undefined` when there is nothing worth exposing. */
3
+ export declare function serializableMeta(meta: Record<string, unknown> | undefined): Record<string, unknown> | undefined;
4
+ /**
5
+ * Builds the discovery view of the manifest — what `__PRISM_MANIFEST__` exposes.
6
+ *
7
+ * It is deliberately not the runtime manifest: no Angular class references, no
8
+ * providers, no scanned input/output metadata. It carries what an external tool
9
+ * needs to enumerate and address variants, plus the `@Showcase` metadata that
10
+ * lets a tool decide how to treat one.
11
+ */
12
+ export declare function buildDiscoveryManifest(manifest: RuntimeManifest): DiscoveryManifest;
13
+ //# sourceMappingURL=discovery-manifest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discovery-manifest.d.ts","sourceRoot":"","sources":["../../src/app/discovery-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,iBAAiB,EAEjB,eAAe,EAChB,MAAM,2BAA2B,CAAC;AAoDnC,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,GACxC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAOrC;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,eAAe,GACxB,iBAAiB,CAsCnB"}
@@ -0,0 +1,106 @@
1
+ import { resolveVariantBg } from '../shared/variant-bg.js';
2
+ /**
3
+ * External tooling reads `__PRISM_MANIFEST__` through a structured-clone bridge
4
+ * (`page.evaluate()` in Playwright, `postMessage`, devtools). `ShowcaseConfig.meta`
5
+ * is declared as an open `Record<string, unknown>`, so a consumer is free to put
6
+ * a component class, a function or a cyclic object in there — any of which makes
7
+ * that bridge throw or silently drop the whole payload.
8
+ *
9
+ * Everything crossing into the global is therefore reduced to plain JSON-safe
10
+ * values: primitives, arrays, and objects with a plain prototype. Class
11
+ * instances (Angular types included), functions, symbols, Dates, Maps and DOM
12
+ * nodes are dropped rather than half-serialised, and cycles are broken.
13
+ *
14
+ * Array elements become `null` when they cannot be represented, matching
15
+ * `JSON.stringify` so indices stay stable.
16
+ */
17
+ function toSerializable(value, seen) {
18
+ if (value === null)
19
+ return null;
20
+ const type = typeof value;
21
+ if (type === 'string' || type === 'boolean')
22
+ return value;
23
+ if (type === 'number')
24
+ return Number.isFinite(value) ? value : undefined;
25
+ if (type !== 'object')
26
+ return undefined;
27
+ const object = value;
28
+ if (seen.has(object))
29
+ return undefined;
30
+ if (Array.isArray(object)) {
31
+ seen.add(object);
32
+ const items = object.map((item) => {
33
+ const serialized = toSerializable(item, seen);
34
+ return serialized === undefined ? null : serialized;
35
+ });
36
+ seen.delete(object);
37
+ return items;
38
+ }
39
+ const proto = Object.getPrototypeOf(object);
40
+ if (proto !== Object.prototype && proto !== null)
41
+ return undefined;
42
+ seen.add(object);
43
+ const result = {};
44
+ for (const [key, item] of Object.entries(object)) {
45
+ const serialized = toSerializable(item, seen);
46
+ if (serialized !== undefined)
47
+ result[key] = serialized;
48
+ }
49
+ seen.delete(object);
50
+ return result;
51
+ }
52
+ /** Sanitised `meta`, or `undefined` when there is nothing worth exposing. */
53
+ export function serializableMeta(meta) {
54
+ if (!meta)
55
+ return undefined;
56
+ const serialized = toSerializable(meta, new Set());
57
+ if (!serialized || Object.keys(serialized).length === 0)
58
+ return undefined;
59
+ return serialized;
60
+ }
61
+ /**
62
+ * Builds the discovery view of the manifest — what `__PRISM_MANIFEST__` exposes.
63
+ *
64
+ * It is deliberately not the runtime manifest: no Angular class references, no
65
+ * providers, no scanned input/output metadata. It carries what an external tool
66
+ * needs to enumerate and address variants, plus the `@Showcase` metadata that
67
+ * lets a tool decide how to treat one.
68
+ */
69
+ export function buildDiscoveryManifest(manifest) {
70
+ return {
71
+ components: manifest.components.map((component) => {
72
+ const { className, showcaseConfig } = component.meta;
73
+ // An empty `variants` array is not "no variants": the renderer still
74
+ // instantiates the component with its declared defaults at index 0 and
75
+ // marks it `data-prism-rendered="<class>:0"`. Reporting zero variants
76
+ // here would let a runner walk past a component the app happily renders
77
+ // — the quiet coverage loss the `excluded` status exists to prevent.
78
+ const declared = showcaseConfig.variants ?? [];
79
+ const variants = declared.length
80
+ ? declared.map((variant, index) => {
81
+ const meta = serializableMeta(variant.meta);
82
+ const bg = resolveVariantBg(showcaseConfig, index);
83
+ return meta
84
+ ? { name: variant.name, index, bg, meta }
85
+ : { name: variant.name, index, bg };
86
+ })
87
+ : [
88
+ {
89
+ name: 'Default',
90
+ index: 0,
91
+ bg: resolveVariantBg(showcaseConfig, 0),
92
+ },
93
+ ];
94
+ const meta = serializableMeta(showcaseConfig.meta);
95
+ const discovered = {
96
+ className,
97
+ title: showcaseConfig.title,
98
+ variants,
99
+ };
100
+ if (meta)
101
+ discovered.meta = meta;
102
+ return discovered;
103
+ }),
104
+ pages: (manifest.pages ?? []).map((page) => ({ title: page.title })),
105
+ };
106
+ }
@@ -1,4 +1,19 @@
1
1
  import * as i0 from "@angular/core";
2
+ /** Every name the registry can draw — what a panel's `icon` has to pick from. */
3
+ export declare const ICON_NAMES: readonly string[];
4
+ /**
5
+ * The glyph markup for `name`, or `undefined` when the registry has no entry.
6
+ *
7
+ * A miss is warned about rather than passed over, because the symptom is
8
+ * invisible: the `<svg>` is still built at the right size, so a missing entry
9
+ * renders as a correctly-spaced blank and reads like a CSS bug. Plugins declare
10
+ * `icon` as a free-form string and are compiled separately from this registry,
11
+ * so nothing else catches the typo — the visual regression panel shipped with a
12
+ * blank tab icon exactly this way.
13
+ *
14
+ * Warned once per name: an icon on a panel tab re-renders constantly.
15
+ */
16
+ export declare function resolveIcon(name: string): string | undefined;
2
17
  export declare class PrismIconComponent {
3
18
  readonly name: import("@angular/core").InputSignal<string>;
4
19
  readonly size: import("@angular/core").InputSignal<number>;
@@ -1 +1 @@
1
- {"version":3,"file":"prism-icon.component.d.ts","sourceRoot":"","sources":["../../../src/app/icons/prism-icon.component.ts"],"names":[],"mappings":";AAmDA,qBAOa,kBAAkB;IAC7B,QAAQ,CAAC,IAAI,8CAA4B;IACzC,QAAQ,CAAC,IAAI,8CAAa;IAE1B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAmC;;yCAJ3C,kBAAkB;2CAAlB,kBAAkB;CAkC9B"}
1
+ {"version":3,"file":"prism-icon.component.d.ts","sourceRoot":"","sources":["../../../src/app/icons/prism-icon.component.ts"],"names":[],"mappings":";AAqDA,iFAAiF;AACjF,eAAO,MAAM,UAAU,EAAE,SAAS,MAAM,EAAuB,CAAC;AAIhE;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAW5D;AAED,qBAOa,kBAAkB;IAC7B,QAAQ,CAAC,IAAI,8CAA4B;IACzC,QAAQ,CAAC,IAAI,8CAAa;IAE1B,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAmC;;yCAJ3C,kBAAkB;2CAAlB,kBAAkB;CAkC9B"}
@@ -19,6 +19,7 @@ const ICONS = {
19
19
  'box-select': '<path d="M5 3a2 2 0 0 0-2 2"/><path d="M19 3a2 2 0 0 1 2 2"/><path d="M21 19a2 2 0 0 1-2 2"/><path d="M5 21a2 2 0 0 1-2-2"/><path d="M9 3h1m4 0h1M9 21h1m4 0h1M3 9v1m0 4v1M21 9v1m0 4v1"/>',
20
20
  activity: '<path d="M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2"/>',
21
21
  'shield-check': '<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/>',
22
+ camera: '<path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"/><circle cx="12" cy="13" r="3"/>',
22
23
  move: '<path d="M12 2v20M2 12h20m-16-4-4 4 4 4m12-8 4 4-4 4M8 8l-4 4 4 4"/>',
23
24
  crosshair: '<circle cx="12" cy="12" r="10"/><path d="M22 12h-4M6 12H2m10-6V2m0 20v-4"/>',
24
25
  copy: '<rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>',
@@ -29,6 +30,29 @@ const ICONS = {
29
30
  check: '<path d="M20 6 9 17l-5-5"/>',
30
31
  };
31
32
  const SVG_NS = 'http://www.w3.org/2000/svg';
33
+ /** Every name the registry can draw — what a panel's `icon` has to pick from. */
34
+ export const ICON_NAMES = Object.keys(ICONS);
35
+ const warnedIcons = new Set();
36
+ /**
37
+ * The glyph markup for `name`, or `undefined` when the registry has no entry.
38
+ *
39
+ * A miss is warned about rather than passed over, because the symptom is
40
+ * invisible: the `<svg>` is still built at the right size, so a missing entry
41
+ * renders as a correctly-spaced blank and reads like a CSS bug. Plugins declare
42
+ * `icon` as a free-form string and are compiled separately from this registry,
43
+ * so nothing else catches the typo — the visual regression panel shipped with a
44
+ * blank tab icon exactly this way.
45
+ *
46
+ * Warned once per name: an icon on a panel tab re-renders constantly.
47
+ */
48
+ export function resolveIcon(name) {
49
+ const content = ICONS[name];
50
+ if (content === undefined && !warnedIcons.has(name)) {
51
+ warnedIcons.add(name);
52
+ console.warn(`[ng-prism] Unknown icon "${name}" — rendering an empty glyph. Available: ${ICON_NAMES.join(', ')}.`);
53
+ }
54
+ return content;
55
+ }
32
56
  export class PrismIconComponent {
33
57
  name = input.required(/* @ts-ignore */
34
58
  ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
@@ -39,7 +63,7 @@ export class PrismIconComponent {
39
63
  effect(() => {
40
64
  const iconName = this.name();
41
65
  const iconSize = this.size();
42
- const content = ICONS[iconName] ?? '';
66
+ const content = resolveIcon(iconName) ?? '';
43
67
  const host = this.el.nativeElement;
44
68
  host.innerHTML = '';
45
69
  const svg = document.createElementNS(SVG_NS, 'svg');
@@ -66,4 +90,4 @@ export class PrismIconComponent {
66
90
  type: Component,
67
91
  args: [{ selector: 'prism-icon', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: ``, styles: [":host { display: inline-flex; align-items: center; justify-content: center; }"] }]
68
92
  }], () => [], { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }] }); })();
69
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismIconComponent, { className: "PrismIconComponent", filePath: "app/icons/prism-icon.component.ts", lineNumber: 59 }); })();
93
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismIconComponent, { className: "PrismIconComponent", filePath: "app/icons/prism-icon.component.ts", lineNumber: 91 }); })();
@@ -1 +1 @@
1
- {"version":3,"file":"provide-prism.d.ts","sourceRoot":"","sources":["../../src/app/provide-prism.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAStE,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACzC;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,eAAe,EACzB,MAAM,CAAC,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,oBAAoB,CAuCtB"}
1
+ {"version":3,"file":"provide-prism.d.ts","sourceRoot":"","sources":["../../src/app/provide-prism.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAUtE,MAAM,WAAW,mBAAmB;IAClC,cAAc,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACzC;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,eAAe,EACzB,MAAM,CAAC,EAAE,aAAa,EACtB,OAAO,CAAC,EAAE,mBAAmB,GAC5B,oBAAoB,CAiCtB"}
@@ -1,6 +1,7 @@
1
1
  import { makeEnvironmentProviders, } from '@angular/core';
2
2
  import { provideHighlightOptions } from 'ngx-highlightjs';
3
3
  import { componentPage } from '../plugin/page-helpers.js';
4
+ import { buildDiscoveryManifest } from './discovery-manifest.js';
4
5
  import { BUILTIN_PANELS } from './panels/builtin-panels.js';
5
6
  import { PRISM_BUILTIN_PANELS, PRISM_CONFIG, PRISM_MANIFEST, } from './tokens/prism-tokens.js';
6
7
  export function providePrism(manifest, config, options) {
@@ -13,19 +14,13 @@ export function providePrism(manifest, config, options) {
13
14
  ],
14
15
  }
15
16
  : manifest;
16
- // Exposes a thin discovery view of the manifest to external audit tooling.
17
- // We intentionally only expose className + variant info no Angular type references.
17
+ // Exposes a thin discovery view of the manifest to external audit tooling:
18
+ // class name, title, variants, and the `@Showcase` metadata a tool needs to
19
+ // decide how to treat a variant. Still no Angular type references — see
20
+ // buildDiscoveryManifest for what gets stripped and why.
18
21
  if (typeof globalThis !== 'undefined') {
19
- globalThis['__PRISM_MANIFEST__'] = {
20
- components: mergedManifest.components.map((c) => ({
21
- className: c.meta.className,
22
- variants: c.meta.showcaseConfig.variants?.map((v, i) => ({
23
- name: v.name,
24
- index: i,
25
- })) ?? [{ name: 'Default', index: 0 }],
26
- })),
27
- pages: (mergedManifest.pages ?? []).map((p) => ({ title: p.title })),
28
- };
22
+ globalThis['__PRISM_MANIFEST__'] =
23
+ buildDiscoveryManifest(mergedManifest);
29
24
  }
30
25
  return makeEnvironmentProviders([
31
26
  { provide: PRISM_MANIFEST, useValue: mergedManifest },
@@ -0,0 +1,25 @@
1
+ import type { Type } from '@angular/core';
2
+ import type { PanelDefinition } from '../../plugin/plugin.types.js';
3
+ /** What the renderer should show in the overlay slot inside `.demo-wrap`. */
4
+ export type OverlayResolution = {
5
+ kind: 'none';
6
+ } | {
7
+ kind: 'eager';
8
+ component: Type<unknown>;
9
+ } | {
10
+ kind: 'lazy';
11
+ panelId: string;
12
+ load: () => Promise<Type<unknown>>;
13
+ };
14
+ /**
15
+ * Decides which overlay component belongs to the active panel.
16
+ *
17
+ * Panel overlays render *inside* `.demo-wrap` — the same element external
18
+ * screenshot tools capture — so capture mode resolves to `none` regardless of
19
+ * which panel is active, and never triggers a lazy overlay load.
20
+ */
21
+ export declare function resolveOverlay(panels: readonly PanelDefinition[], activePanelId: string, options: {
22
+ captureActive: boolean;
23
+ cache: ReadonlyMap<string, Type<unknown>>;
24
+ }): OverlayResolution;
25
+ //# sourceMappingURL=overlay-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlay-resolver.d.ts","sourceRoot":"","sources":["../../../src/app/renderer/overlay-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE,6EAA6E;AAC7E,MAAM,MAAM,iBAAiB,GACzB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,CAAA;CAAE,GAC3C;IACE,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;CACpC,CAAC;AAIN;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,SAAS,eAAe,EAAE,EAClC,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE;IACP,aAAa,EAAE,OAAO,CAAC;IACvB,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;CAC3C,GACA,iBAAiB,CAkBnB"}
@@ -0,0 +1,25 @@
1
+ const NONE = { kind: 'none' };
2
+ /**
3
+ * Decides which overlay component belongs to the active panel.
4
+ *
5
+ * Panel overlays render *inside* `.demo-wrap` — the same element external
6
+ * screenshot tools capture — so capture mode resolves to `none` regardless of
7
+ * which panel is active, and never triggers a lazy overlay load.
8
+ */
9
+ export function resolveOverlay(panels, activePanelId, options) {
10
+ if (options.captureActive)
11
+ return NONE;
12
+ const panel = panels.find((p) => p.id === activePanelId);
13
+ if (!panel)
14
+ return NONE;
15
+ if (panel.overlayComponent) {
16
+ return { kind: 'eager', component: panel.overlayComponent };
17
+ }
18
+ if (panel.loadOverlayComponent) {
19
+ const cached = options.cache.get(panel.id);
20
+ return cached
21
+ ? { kind: 'eager', component: cached }
22
+ : { kind: 'lazy', panelId: panel.id, load: panel.loadOverlayComponent };
23
+ }
24
+ return NONE;
25
+ }
@@ -2,6 +2,7 @@ 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 { PrismCaptureService } from '../services/prism-capture.service.js';
5
6
  import { PrismVariantBgService } from '../services/prism-variant-bg.service.js';
6
7
  import * as i0 from "@angular/core";
7
8
  export declare class PrismRendererComponent {
@@ -9,6 +10,7 @@ export declare class PrismRendererComponent {
9
10
  protected readonly navigationService: PrismNavigationService;
10
11
  protected readonly rendererService: PrismRendererService;
11
12
  protected readonly canvasService: PrismCanvasService;
13
+ protected readonly capture: PrismCaptureService;
12
14
  protected readonly variantBg: PrismVariantBgService;
13
15
  private readonly eventLogService;
14
16
  private readonly manifestService;
@@ -1 +1 @@
1
- {"version":3,"file":"prism-renderer.component.d.ts","sourceRoot":"","sources":["../../../src/app/renderer/prism-renderer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,QAAQ,EAER,KAAK,IAAI,EAIV,MAAM,eAAe,CAAC;AAYvB,OAAO,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AAGjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;;AAKhF,qBA8Ia,sBAAsB;IACjC,SAAS,CAAC,QAAQ,CAAC,IAAI,OAAQ;IAC/B,SAAS,CAAC,QAAQ,CAAC,iBAAiB,yBAAkC;IACtE,SAAS,CAAC,QAAQ,CAAC,eAAe,uBAAgC;IAClE,SAAS,CAAC,QAAQ,CAAC,aAAa,qBAA8B;IAC9D,SAAS,CAAC,QAAQ,CAAC,SAAS,wBAAiC;IAC7D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsB;IACjD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmC;IACxD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAE3B;IAEH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA8B;IAE5D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAEpB;IACH,OAAO,CAAC,YAAY,CAAsC;IAC1D,OAAO,CAAC,mBAAmB,CAAsC;IACjE,OAAO,CAAC,oBAAoB,CAChB;IACZ,OAAO,CAAC,YAAY,CAAS;IAE7B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoC;IACjE,QAAQ,CAAC,aAAa,+DAAsC;IAC5D,wFAAwF;IACxF,SAAS,CAAC,QAAQ,CAAC,WAAW,gDAQ3B;IACH,6GAA6G;IAC7G,SAAS,CAAC,QAAQ,CAAC,YAAY,4FAU5B;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa;;MAA6C;IAC7E,SAAS,CAAC,QAAQ,CAAC,eAAe,yFAG/B;;IA0FH,OAAO,CAAC,eAAe;IA6FvB,OAAO,CAAC,OAAO;yCA9OJ,sBAAsB;2CAAtB,sBAAsB;CA4PlC"}
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,mBAAmB,EAAE,MAAM,sCAAsC,CAAC;AAC3E,OAAO,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;;AAMhF,qBAqJa,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,OAAO,sBAA+B;IACzD,SAAS,CAAC,QAAQ,CAAC,SAAS,wBAAiC;IAC7D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAgC;IAChE,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoB;IAC7C,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAsB;IACjD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAmC;IACxD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAE3B;IAEH,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAC1D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA8B;IAE5D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAEpB;IACH,OAAO,CAAC,YAAY,CAAsC;IAC1D,OAAO,CAAC,mBAAmB,CAAsC;IACjE,OAAO,CAAC,oBAAoB,CAChB;IACZ,OAAO,CAAC,YAAY,CAAS;IAE7B,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAoC;IACjE,QAAQ,CAAC,aAAa,+DAAsC;IAC5D,wFAAwF;IACxF,SAAS,CAAC,QAAQ,CAAC,WAAW,gDAQ3B;IACH,6GAA6G;IAC7G,SAAS,CAAC,QAAQ,CAAC,YAAY,4FAU5B;IACH,SAAS,CAAC,QAAQ,CAAC,aAAa;;MAA6C;IAC7E,SAAS,CAAC,QAAQ,CAAC,eAAe,yFAG/B;;IAmFH,OAAO,CAAC,eAAe;IA6FvB,OAAO,CAAC,OAAO;yCAxOJ,sBAAsB;2CAAtB,sBAAsB;CAsPlC"}
@@ -9,17 +9,31 @@ 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 { PrismCaptureService } from '../services/prism-capture.service.js';
12
13
  import { PrismVariantBgService } from '../services/prism-variant-bg.service.js';
13
14
  import { PrismCanvasRulersComponent } from '../canvas/prism-canvas-rulers.component.js';
14
15
  import { PrismCanvasBgPillComponent } from '../canvas/prism-canvas-bg-pill.component.js';
15
16
  import { buildKnownInputs } from './known-inputs.js';
17
+ import { resolveOverlay } from './overlay-resolver.js';
16
18
  import * as i0 from "@angular/core";
17
19
  const _c0 = ["outlet"];
18
- function PrismRendererComponent_Conditional_10_ng_container_0_Template(rf, ctx) { if (rf & 1) {
20
+ function PrismRendererComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
21
+ i0.ɵɵelementStart(0, "div", 3)(1, "span", 4);
22
+ i0.ɵɵtext(2);
23
+ i0.ɵɵelementEnd()();
24
+ i0.ɵɵelement(3, "div", 5)(4, "prism-canvas-rulers")(5, "prism-canvas-bg-pill");
25
+ } if (rf & 2) {
26
+ const ctx_r0 = i0.ɵɵnextContext();
27
+ i0.ɵɵadvance(2);
28
+ i0.ɵɵtextInterpolate1("", ctx_r0.Math.round(ctx_r0.canvasService.zoom() * 100), "%");
29
+ i0.ɵɵadvance();
30
+ i0.ɵɵclassProp("visible", ctx_r0.canvasService.guides());
31
+ } }
32
+ function PrismRendererComponent_Conditional_5_ng_container_0_Template(rf, ctx) { if (rf & 1) {
19
33
  i0.ɵɵelementContainer(0);
20
34
  } }
21
- function PrismRendererComponent_Conditional_10_Template(rf, ctx) { if (rf & 1) {
22
- i0.ɵɵtemplate(0, PrismRendererComponent_Conditional_10_ng_container_0_Template, 1, 0, "ng-container", 6);
35
+ function PrismRendererComponent_Conditional_5_Template(rf, ctx) { if (rf & 1) {
36
+ i0.ɵɵtemplate(0, PrismRendererComponent_Conditional_5_ng_container_0_Template, 1, 0, "ng-container", 6);
23
37
  } if (rf & 2) {
24
38
  const ctx_r0 = i0.ɵɵnextContext();
25
39
  i0.ɵɵproperty("ngComponentOutlet", ctx_r0.activeOverlay())("ngComponentOutletInputs", ctx_r0.overlayInputs)("ngComponentOutletInjector", ctx_r0.overlayInjector());
@@ -29,6 +43,7 @@ export class PrismRendererComponent {
29
43
  navigationService = inject(PrismNavigationService);
30
44
  rendererService = inject(PrismRendererService);
31
45
  canvasService = inject(PrismCanvasService);
46
+ capture = inject(PrismCaptureService);
32
47
  variantBg = inject(PrismVariantBgService);
33
48
  eventLogService = inject(PrismEventLogService);
34
49
  manifestService = inject(PrismManifestService);
@@ -123,32 +138,24 @@ export class PrismRendererComponent {
123
138
  ...BUILTIN_PANELS,
124
139
  ...this.pluginService.panels(),
125
140
  ];
126
- const panel = allPanels.find((p) => p.id === panelId) ?? null;
127
- if (!panel) {
128
- this.activeOverlay.set(null);
141
+ const resolution = resolveOverlay(allPanels, panelId, {
142
+ captureActive: this.capture.active(),
143
+ cache: this.overlayCache,
144
+ });
145
+ if (resolution.kind === 'eager') {
146
+ this.activeOverlay.set(resolution.component);
129
147
  return;
130
148
  }
131
- if (panel.overlayComponent) {
132
- this.activeOverlay.set(panel.overlayComponent);
149
+ this.activeOverlay.set(null);
150
+ if (resolution.kind !== 'lazy')
133
151
  return;
134
- }
135
- if (panel.loadOverlayComponent) {
136
- const cached = this.overlayCache.get(panel.id);
137
- if (cached) {
138
- this.activeOverlay.set(cached);
139
- return;
152
+ const { panelId: requestedPanelId, load } = resolution;
153
+ load().then((c) => {
154
+ this.overlayCache.set(requestedPanelId, c);
155
+ if (this.panelService.activePanelId() === requestedPanelId) {
156
+ this.activeOverlay.set(c);
140
157
  }
141
- this.activeOverlay.set(null);
142
- const requestedPanelId = panel.id;
143
- panel.loadOverlayComponent().then((c) => {
144
- this.overlayCache.set(requestedPanelId, c);
145
- if (this.panelService.activePanelId() === requestedPanelId) {
146
- this.activeOverlay.set(c);
147
- }
148
- });
149
- return;
150
- }
151
- this.activeOverlay.set(null);
158
+ });
152
159
  });
153
160
  this.destroyRef.onDestroy(() => this.cleanup());
154
161
  }
@@ -230,29 +237,25 @@ export class PrismRendererComponent {
230
237
  i0.ɵɵviewQuerySignal(ctx.outlet, _c0, 5, ViewContainerRef);
231
238
  } if (rf & 2) {
232
239
  i0.ɵɵqueryAdvance();
233
- } }, decls: 11, vars: 10, 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) {
234
- i0.ɵɵelementStart(0, "div", 1)(1, "div", 2)(2, "span", 3);
235
- i0.ɵɵtext(3);
236
- i0.ɵɵelementEnd()();
237
- i0.ɵɵelement(4, "div", 4)(5, "prism-canvas-rulers")(6, "prism-canvas-bg-pill");
238
- i0.ɵɵelementStart(7, "div", 5);
239
- i0.ɵɵelementContainer(8, null, 0);
240
- i0.ɵɵconditionalCreate(10, PrismRendererComponent_Conditional_10_Template, 1, 3, "ng-container");
240
+ } }, decls: 6, vars: 8, consts: [["outlet", ""], [1, "prism-canvas-stage"], [1, "demo-wrap"], [1, "canvas-badges"], [1, "c-badge"], [1, "stage-crosshair"], [4, "ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector"]], template: function PrismRendererComponent_Template(rf, ctx) { if (rf & 1) {
241
+ i0.ɵɵelementStart(0, "div", 1);
242
+ i0.ɵɵconditionalCreate(1, PrismRendererComponent_Conditional_1_Template, 6, 3);
243
+ i0.ɵɵelementStart(2, "div", 2);
244
+ i0.ɵɵelementContainer(3, null, 0);
245
+ i0.ɵɵconditionalCreate(5, PrismRendererComponent_Conditional_5_Template, 1, 3, "ng-container");
241
246
  i0.ɵɵelementEnd()();
242
247
  } if (rf & 2) {
243
248
  i0.ɵɵattribute("data-bg", ctx.variantBg.effective())("data-rulers", ctx.canvasService.rulers() ? "" : null);
244
- i0.ɵɵadvance(3);
245
- i0.ɵɵtextInterpolate1("", ctx.Math.round(ctx.canvasService.zoom() * 100), "%");
246
249
  i0.ɵɵadvance();
247
- i0.ɵɵclassProp("visible", ctx.canvasService.guides());
248
- i0.ɵɵadvance(3);
250
+ i0.ɵɵconditional(!ctx.capture.active() ? 1 : -1);
251
+ i0.ɵɵadvance();
249
252
  i0.ɵɵstyleProp("--zoom", ctx.canvasService.zoom());
250
253
  i0.ɵɵattribute("data-prism-rendered", ctx.renderedKey())("data-canvas-layout", ctx.canvasLayout());
251
254
  i0.ɵɵadvance(3);
252
- i0.ɵɵconditional(ctx.activeOverlay() ? 10 : -1);
255
+ i0.ɵɵconditional(ctx.activeOverlay() ? 5 : -1);
253
256
  } }, dependencies: [PrismCanvasRulersComponent,
254
257
  PrismCanvasBgPillComponent,
255
- 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 --prism-canvas-overlay-top: 12px;\n --prism-canvas-overlay-inline: 20px;\n }\n .prism-canvas-stage[data-rulers][_ngcontent-%COMP%] {\n --prism-canvas-overlay-top: 28px;\n --prism-canvas-overlay-inline: 28px;\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: var(--prism-canvas-overlay-top);\n left: var(--prism-canvas-overlay-inline);\n display: flex;\n gap: 6px;\n pointer-events: none;\n transition: top var(--dur-base), left var(--dur-base);\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 display: inline-block;\n transform: scale(var(--zoom, 1));\n transition: transform 0.18s;\n }\n .demo-wrap[data-canvas-layout=\"stretch\"][_ngcontent-%COMP%] {\n display: block;\n width: 100%;\n max-width: 800px;\n }"] });
258
+ 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 --prism-canvas-overlay-top: 12px;\n --prism-canvas-overlay-inline: 20px;\n }\n .prism-canvas-stage[data-rulers][_ngcontent-%COMP%] {\n --prism-canvas-overlay-top: 28px;\n --prism-canvas-overlay-inline: 28px;\n }\n\n .prism-canvas-stage[data-bg=\"plain\"][_ngcontent-%COMP%] {\n background-image: none;\n }\n \n\n\n\n\n .prism-canvas-stage[data-bg=\"light\"][_ngcontent-%COMP%] {\n background-color: var(--prism-void-light, #f7f5fc);\n background-image: none;\n }\n .prism-canvas-stage[data-bg=\"dark\"][_ngcontent-%COMP%] {\n background-color: var(--prism-void-dark, #07050f);\n background-image: none;\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: var(--prism-canvas-overlay-top);\n left: var(--prism-canvas-overlay-inline);\n display: flex;\n gap: 6px;\n pointer-events: none;\n transition: top var(--dur-base), left var(--dur-base);\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 display: inline-block;\n transform: scale(var(--zoom, 1));\n transition: transform 0.18s;\n }\n .demo-wrap[data-canvas-layout=\"stretch\"][_ngcontent-%COMP%] {\n display: block;\n width: 100%;\n max-width: 800px;\n }"] });
256
259
  }
257
260
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PrismRendererComponent, [{
258
261
  type: Component,
@@ -266,6 +269,7 @@ export class PrismRendererComponent {
266
269
  [attr.data-bg]="variantBg.effective()"
267
270
  [attr.data-rulers]="canvasService.rulers() ? '' : null"
268
271
  >
272
+ @if (!capture.active()) {
269
273
  <div class="canvas-badges">
270
274
  <span class="c-badge"
271
275
  >{{ Math.round(canvasService.zoom() * 100) }}%</span
@@ -277,6 +281,7 @@ export class PrismRendererComponent {
277
281
  ></div>
278
282
  <prism-canvas-rulers />
279
283
  <prism-canvas-bg-pill />
284
+ }
280
285
 
281
286
  <div
282
287
  class="demo-wrap"
@@ -296,11 +301,11 @@ export class PrismRendererComponent {
296
301
  }
297
302
  </div>
298
303
  </div>
299
- `, 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 --prism-canvas-overlay-top: 12px;\n --prism-canvas-overlay-inline: 20px;\n }\n .prism-canvas-stage[data-rulers] {\n --prism-canvas-overlay-top: 28px;\n --prism-canvas-overlay-inline: 28px;\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: var(--prism-canvas-overlay-top);\n left: var(--prism-canvas-overlay-inline);\n display: flex;\n gap: 6px;\n pointer-events: none;\n transition: top var(--dur-base), left var(--dur-base);\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 display: inline-block;\n transform: scale(var(--zoom, 1));\n transition: transform 0.18s;\n }\n .demo-wrap[data-canvas-layout=\"stretch\"] {\n display: block;\n width: 100%;\n max-width: 800px;\n }\n\n "] }]
304
+ `, 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 --prism-canvas-overlay-top: 12px;\n --prism-canvas-overlay-inline: 20px;\n }\n .prism-canvas-stage[data-rulers] {\n --prism-canvas-overlay-top: 28px;\n --prism-canvas-overlay-inline: 28px;\n }\n\n .prism-canvas-stage[data-bg=\"plain\"] {\n background-image: none;\n }\n /* Flat, not dotted. \"light\" and \"dark\" name a surface a component was\n designed against \u2014 and they are the two backgrounds whose colour is\n absolute rather than a theme token, which is what makes them the values\n to declare for a screenshot baseline. \"dots\" already exists for anyone\n who wants the grid. */\n .prism-canvas-stage[data-bg=\"light\"] {\n background-color: var(--prism-void-light, #f7f5fc);\n background-image: none;\n }\n .prism-canvas-stage[data-bg=\"dark\"] {\n background-color: var(--prism-void-dark, #07050f);\n background-image: none;\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: var(--prism-canvas-overlay-top);\n left: var(--prism-canvas-overlay-inline);\n display: flex;\n gap: 6px;\n pointer-events: none;\n transition: top var(--dur-base), left var(--dur-base);\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 display: inline-block;\n transform: scale(var(--zoom, 1));\n transition: transform 0.18s;\n }\n .demo-wrap[data-canvas-layout=\"stretch\"] {\n display: block;\n width: 100%;\n max-width: 800px;\n }\n\n "] }]
300
305
  }], () => [], { outlet: [{ type: i0.ViewChild, args: ['outlet', { ...{
301
306
  read: ViewContainerRef,
302
307
  }, isSignal: true }] }] }); })();
303
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismRendererComponent, { className: "PrismRendererComponent", filePath: "app/renderer/prism-renderer.component.ts", lineNumber: 180 }); })();
308
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PrismRendererComponent, { className: "PrismRendererComponent", filePath: "app/renderer/prism-renderer.component.ts", lineNumber: 189 }); })();
304
309
  // SAFETY: `content` originates from `@Showcase({ variants: [{ content }] })`
305
310
  // in developer-authored source code. It is trusted by ng-prism's threat model
306
311
  // (see SECURITY.md). Sanitization would strip the Angular component/directive
@@ -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;AAwBD,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,CAmER"}
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;AAwBD,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,CA0ER"}
@@ -6,7 +6,7 @@ function pushAttribute(attributes, name, value) {
6
6
  attributes.push(`${name}="${escapeAttrValue(value)}"`);
7
7
  }
8
8
  else if (typeof value === 'boolean') {
9
- attributes.push(`[${name}]="true"`);
9
+ attributes.push(`[${name}]="${value}"`);
10
10
  }
11
11
  else if (typeof value === 'number') {
12
12
  attributes.push(`[${name}]="${value}"`);
@@ -29,12 +29,16 @@ export function generateSnippet(selector, inputs, values, explicitKeys, content,
29
29
  const value = values[input.name];
30
30
  if (value === undefined)
31
31
  continue;
32
- if (typeof value === 'boolean' && !value)
33
- continue;
34
32
  if (input.defaultValue !== undefined &&
35
33
  value === input.defaultValue &&
36
34
  !explicitKeys?.has(input.name))
37
35
  continue;
36
+ // Only a guess, and only sound when no default is known: a boolean input
37
+ // that defaults to `true` has to show its explicit `false`.
38
+ if (input.defaultValue === undefined &&
39
+ typeof value === 'boolean' &&
40
+ !value)
41
+ continue;
38
42
  pushAttribute(attributes, input.name, value);
39
43
  }
40
44
  for (const [name, value] of Object.entries(values)) {
@@ -108,12 +112,16 @@ function generateDirectiveSnippet(selector, inputs, values, explicitKeys, conten
108
112
  const value = values[input.name];
109
113
  if (value === undefined)
110
114
  continue;
111
- if (typeof value === 'boolean' && !value)
112
- continue;
113
115
  if (input.defaultValue !== undefined &&
114
116
  value === input.defaultValue &&
115
117
  !explicitKeys?.has(input.name))
116
118
  continue;
119
+ // Only a guess, and only sound when no default is known: a boolean input
120
+ // that defaults to `true` has to show its explicit `false`.
121
+ if (input.defaultValue === undefined &&
122
+ typeof value === 'boolean' &&
123
+ !value)
124
+ continue;
117
125
  pushAttribute(attributes, input.name, value);
118
126
  }
119
127
  for (const [name, value] of Object.entries(values)) {
@@ -6,6 +6,7 @@ export declare class PrismCanvasService {
6
6
  readonly zoom: import("@angular/core").WritableSignal<number>;
7
7
  readonly guides: import("@angular/core").WritableSignal<boolean>;
8
8
  readonly rulers: import("@angular/core").WritableSignal<boolean>;
9
+ private readonly capture;
9
10
  constructor();
10
11
  setBg(bg: CanvasBg): void;
11
12
  setZoom(z: number): void;
@@ -1 +1 @@
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
+ {"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;;AAG3E,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;IAEhC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;;IAWvD,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;yCAnDD,kBAAkB;6CAAlB,kBAAkB;CAiE9B"}
@@ -1,5 +1,6 @@
1
- import { Injectable, signal } from '@angular/core';
1
+ import { inject, Injectable, signal } from '@angular/core';
2
2
  import { CANVAS_BGS } from '../../shared/canvas-bg.type.js';
3
+ import { PrismCaptureService } from './prism-capture.service.js';
3
4
  import * as i0 from "@angular/core";
4
5
  const STORAGE_KEY = 'ng-prism-canvas';
5
6
  export class PrismCanvasService {
@@ -11,7 +12,14 @@ export class PrismCanvasService {
11
12
  ...(ngDevMode ? [{ debugName: "guides" }] : /* istanbul ignore next */ []));
12
13
  rulers = signal(false, /* @ts-ignore */
13
14
  ...(ngDevMode ? [{ debugName: "rulers" }] : /* istanbul ignore next */ []));
15
+ capture = inject(PrismCaptureService);
14
16
  constructor() {
17
+ // Capture mode renders for a screenshot tool: persisted zoom, guides and
18
+ // rulers would leak a previous session's state into the image, so the
19
+ // defaults (zoom 1, no guides, no rulers) are kept and nothing is written
20
+ // back.
21
+ if (this.capture.active())
22
+ return;
15
23
  this.loadFromStorage();
16
24
  }
17
25
  setBg(bg) {
@@ -49,6 +57,8 @@ export class PrismCanvasService {
49
57
  catch { }
50
58
  }
51
59
  save() {
60
+ if (this.capture.active())
61
+ return;
52
62
  try {
53
63
  localStorage.setItem(STORAGE_KEY, JSON.stringify({
54
64
  bg: this.bg(),
@@ -0,0 +1,42 @@
1
+ import * as i0 from "@angular/core";
2
+ /** URL query parameter that switches the Prism canvas into capture-isolation mode. */
3
+ export declare const CAPTURE_PARAM = "capture";
4
+ /** Attribute set on `<html>` while capture mode is active. */
5
+ export declare const CAPTURE_ATTRIBUTE = "data-prism-capture";
6
+ /**
7
+ * Capture-isolation mode.
8
+ *
9
+ * Screenshot tools clip the composited page to the capture target's box, so
10
+ * anything painted behind or inside `.demo-wrap` ends up in the image. Capture
11
+ * mode strips the canvas background down to its opaque, patternless colour —
12
+ * keeping whatever `@Showcase({ bg })` declared — suppresses every piece of
13
+ * canvas chrome, locks zoom to 1 and freezes animation, so a variant renders
14
+ * identically on every run.
15
+ *
16
+ * The flag is read once from the URL at construction time and never written
17
+ * back — {@link PrismUrlStateService} rebuilds the query string from scratch,
18
+ * so navigating inside the app drops `?capture=1` from the address bar while
19
+ * the mode itself stays on for the lifetime of the document.
20
+ *
21
+ * Reading in the constructor (rather than during `PrismUrlStateService.init()`)
22
+ * is deliberate: `PrismCanvasService` restores persisted zoom in *its* own
23
+ * constructor, which can run first, so anything init-order dependent would
24
+ * make zoom-locking racy.
25
+ */
26
+ export declare class PrismCaptureService {
27
+ private readonly _active;
28
+ /** True while the app renders for an external screenshot tool. */
29
+ readonly active: import("@angular/core").Signal<boolean>;
30
+ constructor();
31
+ private applyToDocument;
32
+ static ɵfac: i0.ɵɵFactoryDeclaration<PrismCaptureService, never>;
33
+ static ɵprov: i0.ɵɵInjectableDeclaration<PrismCaptureService>;
34
+ }
35
+ /**
36
+ * Parses the capture flag out of a `window.location.search` string.
37
+ *
38
+ * Accepts `?capture`, `?capture=1` and `?capture=true` as "on"; anything else
39
+ * (including `?capture=0` and `?capture=false`) is "off".
40
+ */
41
+ export declare function parseCaptureParam(search: string): boolean;
42
+ //# sourceMappingURL=prism-capture.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prism-capture.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-capture.service.ts"],"names":[],"mappings":";AAEA,sFAAsF;AACtF,eAAO,MAAM,aAAa,YAAY,CAAC;AAEvC,8DAA8D;AAC9D,eAAO,MAAM,iBAAiB,uBAAuB,CAAC;AAqCtD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,qBACa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAItB;IAEF,kEAAkE;IAClE,QAAQ,CAAC,MAAM,0CAA6B;;IAM5C,OAAO,CAAC,eAAe;yCAdZ,mBAAmB;6CAAnB,mBAAmB;CAuB/B;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAIzD"}
@@ -0,0 +1,100 @@
1
+ import { Injectable, signal } from '@angular/core';
2
+ import * as i0 from "@angular/core";
3
+ /** URL query parameter that switches the Prism canvas into capture-isolation mode. */
4
+ export const CAPTURE_PARAM = 'capture';
5
+ /** Attribute set on `<html>` while capture mode is active. */
6
+ export const CAPTURE_ATTRIBUTE = 'data-prism-capture';
7
+ const STYLE_ELEMENT_ID = 'ng-prism-capture-styles';
8
+ /**
9
+ * Global stylesheet applied in capture mode.
10
+ *
11
+ * This has to be a document-level stylesheet rather than a component `styles`
12
+ * block: Angular's emulated view encapsulation scopes component styles to that
13
+ * component's own content attribute, so a rule written inside the renderer
14
+ * could never reach into the dynamically created showcase component to stop
15
+ * *its* transitions and animations.
16
+ *
17
+ * Stripping `background-image` is what makes the canvas capture-safe without
18
+ * flattening it to one colour. Every `data-bg` value is a background *colour*
19
+ * plus, for all but `plain`, a repeating *pattern* — dots or a checkerboard.
20
+ * The colour is what `@Showcase({ bg })` declares and is deterministic; the
21
+ * pattern is not, because the component is centred, so the pattern's phase
22
+ * beneath it shifts whenever the component changes size and every pixel behind
23
+ * it differs after an unrelated resize. `!important` is required: the stage's
24
+ * own rules are component-scoped and therefore more specific than this one.
25
+ */
26
+ const CAPTURE_STYLES = `
27
+ [${CAPTURE_ATTRIBUTE}] *,
28
+ [${CAPTURE_ATTRIBUTE}] *::before,
29
+ [${CAPTURE_ATTRIBUTE}] *::after {
30
+ transition: none !important;
31
+ animation: none !important;
32
+ caret-color: transparent !important;
33
+ scroll-behavior: auto !important;
34
+ }
35
+
36
+ [${CAPTURE_ATTRIBUTE}] .prism-canvas-stage {
37
+ background-image: none !important;
38
+ }
39
+ `;
40
+ /**
41
+ * Capture-isolation mode.
42
+ *
43
+ * Screenshot tools clip the composited page to the capture target's box, so
44
+ * anything painted behind or inside `.demo-wrap` ends up in the image. Capture
45
+ * mode strips the canvas background down to its opaque, patternless colour —
46
+ * keeping whatever `@Showcase({ bg })` declared — suppresses every piece of
47
+ * canvas chrome, locks zoom to 1 and freezes animation, so a variant renders
48
+ * identically on every run.
49
+ *
50
+ * The flag is read once from the URL at construction time and never written
51
+ * back — {@link PrismUrlStateService} rebuilds the query string from scratch,
52
+ * so navigating inside the app drops `?capture=1` from the address bar while
53
+ * the mode itself stays on for the lifetime of the document.
54
+ *
55
+ * Reading in the constructor (rather than during `PrismUrlStateService.init()`)
56
+ * is deliberate: `PrismCanvasService` restores persisted zoom in *its* own
57
+ * constructor, which can run first, so anything init-order dependent would
58
+ * make zoom-locking racy.
59
+ */
60
+ export class PrismCaptureService {
61
+ _active = signal(typeof window !== 'undefined'
62
+ ? parseCaptureParam(window.location.search)
63
+ : false, /* @ts-ignore */
64
+ ...(ngDevMode ? [{ debugName: "_active" }] : /* istanbul ignore next */ []));
65
+ /** True while the app renders for an external screenshot tool. */
66
+ active = this._active.asReadonly();
67
+ constructor() {
68
+ if (this._active())
69
+ this.applyToDocument();
70
+ }
71
+ applyToDocument() {
72
+ if (typeof document === 'undefined')
73
+ return;
74
+ document.documentElement.setAttribute(CAPTURE_ATTRIBUTE, '');
75
+ if (document.getElementById(STYLE_ELEMENT_ID))
76
+ return;
77
+ const style = document.createElement('style');
78
+ style.id = STYLE_ELEMENT_ID;
79
+ style.textContent = CAPTURE_STYLES;
80
+ document.head.appendChild(style);
81
+ }
82
+ static ɵfac = function PrismCaptureService_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PrismCaptureService)(); };
83
+ static ɵprov = /*@__PURE__*/ i0.ɵɵdefineInjectable({ token: PrismCaptureService, factory: PrismCaptureService.ɵfac, providedIn: 'root' });
84
+ }
85
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PrismCaptureService, [{
86
+ type: Injectable,
87
+ args: [{ providedIn: 'root' }]
88
+ }], () => [], null); })();
89
+ /**
90
+ * Parses the capture flag out of a `window.location.search` string.
91
+ *
92
+ * Accepts `?capture`, `?capture=1` and `?capture=true` as "on"; anything else
93
+ * (including `?capture=0` and `?capture=false`) is "off".
94
+ */
95
+ export function parseCaptureParam(search) {
96
+ const value = new URLSearchParams(search).get(CAPTURE_PARAM);
97
+ if (value === null)
98
+ return false;
99
+ return value === '' || value === '1' || value === 'true';
100
+ }
@@ -1,6 +1,7 @@
1
1
  import * as i0 from "@angular/core";
2
2
  export declare class PrismPersistenceService {
3
3
  private readonly config;
4
+ private readonly capture;
4
5
  private readonly navigationService;
5
6
  private readonly rendererService;
6
7
  private readonly a11yPanelState;
@@ -1 +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"}
1
+ {"version":3,"file":"prism-persistence.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-persistence.service.ts"],"names":[],"mappings":";AA+BA,qBACa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,MAAM,CACyC;IAChE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,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;IAWZ,OAAO,CAAC,kBAAkB;IAmD1B,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,cAAc;IAgBtB,OAAO,CAAC,SAAS;yCA5GN,uBAAuB;6CAAvB,uBAAuB;CA0InC"}
@@ -1,7 +1,8 @@
1
1
  import { effect, inject, Injectable, Injector } from '@angular/core';
2
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';
3
+ import { A11yPanelStateService, } from '../panels/a11y/a11y-panel-state.service.js';
4
+ import { A11yPerspectiveService, } from '../panels/a11y/a11y-perspective.service.js';
5
+ import { PrismCaptureService } from './prism-capture.service.js';
5
6
  import { PrismNavigationService } from './prism-navigation.service.js';
6
7
  import { PrismRendererService } from './prism-renderer.service.js';
7
8
  import * as i0 from "@angular/core";
@@ -10,6 +11,7 @@ const SCHEMA_VERSION = 1;
10
11
  const DEBOUNCE_MS = 200;
11
12
  export class PrismPersistenceService {
12
13
  config = inject(PRISM_CONFIG, { optional: true }) ?? {};
14
+ capture = inject(PrismCaptureService);
13
15
  navigationService = inject(PrismNavigationService);
14
16
  rendererService = inject(PrismRendererService);
15
17
  a11yPanelState = inject(A11yPanelStateService);
@@ -21,6 +23,11 @@ export class PrismPersistenceService {
21
23
  init() {
22
24
  if (this.config.persistState === false)
23
25
  return;
26
+ // Capture mode must render a variant exactly as the manifest declares it;
27
+ // restoring a previous session's control values would silently change what
28
+ // an external screenshot tool captures.
29
+ if (this.capture.active())
30
+ return;
24
31
  const { raw } = this.restoreFromStorage();
25
32
  if (raw !== null)
26
33
  this.lastSerialized = raw;
@@ -46,13 +53,15 @@ export class PrismPersistenceService {
46
53
  if (parsed.a11y?.activeTab && a11yTabs.includes(parsed.a11y.activeTab)) {
47
54
  this.a11yPanelState.activeTab.set(parsed.a11y.activeTab);
48
55
  }
49
- if (parsed.a11y?.perspective === 'visual' || parsed.a11y?.perspective === 'screen-reader') {
56
+ if (parsed.a11y?.perspective === 'visual' ||
57
+ parsed.a11y?.perspective === 'screen-reader') {
50
58
  this.a11yPerspective.mode.set(parsed.a11y.perspective);
51
59
  }
52
60
  const activeComp = this.navigationService.activeComponent();
53
61
  if (activeComp && parsed.inputs) {
54
62
  const bucket = parsed.inputs[activeComp.meta.className];
55
- if (bucket && bucket.variantIndex === this.rendererService.activeVariantIndex()) {
63
+ if (bucket &&
64
+ bucket.variantIndex === this.rendererService.activeVariantIndex()) {
56
65
  const validKeys = new Set(activeComp.meta.inputs.map((i) => i.name));
57
66
  const filtered = {};
58
67
  for (const [k, v] of Object.entries(bucket.values)) {
@@ -119,7 +128,10 @@ export class PrismPersistenceService {
119
128
  }
120
129
  }
121
130
  if (activeComp) {
122
- inputs[activeComp.meta.className] = { variantIndex, values: { ...values } };
131
+ inputs[activeComp.meta.className] = {
132
+ variantIndex,
133
+ values: { ...values },
134
+ };
123
135
  }
124
136
  return {
125
137
  version: SCHEMA_VERSION,
@@ -1 +1 @@
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"}
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;IAqBZ,OAAO,CAAC,cAAc;IA6CtB,OAAO,CAAC,UAAU;yCA5EP,oBAAoB;6CAApB,oBAAoB;CAyHhC"}
@@ -45,7 +45,8 @@ export class PrismUrlStateService {
45
45
  this.suppressSync = true;
46
46
  try {
47
47
  if (componentClassName) {
48
- const comp = this.manifestService.components()
48
+ const comp = this.manifestService
49
+ .components()
49
50
  .find((c) => c.meta.className === componentClassName);
50
51
  if (comp) {
51
52
  this.navigationService.select(comp);
@@ -59,7 +60,8 @@ export class PrismUrlStateService {
59
60
  }
60
61
  }
61
62
  else if (pageTitle) {
62
- const page = this.manifestService.pages()
63
+ const page = this.manifestService
64
+ .pages()
63
65
  .find((p) => p.title === pageTitle);
64
66
  if (page) {
65
67
  this.navigationService.selectPage(page);
@@ -77,6 +79,9 @@ export class PrismUrlStateService {
77
79
  }
78
80
  }
79
81
  writeToUrl(item, variantIndex, viewId, panelId) {
82
+ // Built from scratch, never from the current search string: parameters the
83
+ // app does not own must not survive a navigation. `?capture=1` relies on
84
+ // this — see the "capture flag" specs, which pin the guarantee.
80
85
  const params = new URLSearchParams();
81
86
  if (item?.kind === 'component') {
82
87
  params.set(PARAM_COMPONENT, item.data.meta.className);
@@ -4,9 +4,37 @@ export declare class PrismVariantBgService {
4
4
  private readonly canvas;
5
5
  private readonly navigation;
6
6
  private readonly renderer;
7
+ private readonly capture;
7
8
  readonly recommended: import("@angular/core").Signal<CanvasBg | null>;
8
9
  private readonly _override;
9
10
  readonly override: import("@angular/core").Signal<CanvasBg | null>;
11
+ /**
12
+ * The background the stage actually paints.
13
+ *
14
+ * Capture mode keeps the *declared* background. `@Showcase({ bg })` and a
15
+ * per-variant `bg` exist precisely so a component is judged against the
16
+ * surface it was designed for — an outlined button on `dark` proves nothing
17
+ * when it is screenshotted on the light default — so a capture that dropped
18
+ * them would pin the wrong image.
19
+ *
20
+ * What capture mode does drop is the manual user override: session UI state
21
+ * that must never decide what a baseline looks like. The other half of the
22
+ * determinism problem — the dot grid and the checkerboard, whose phase shifts
23
+ * under a centred component whenever that component changes size — is solved
24
+ * in `CAPTURE_STYLES`, which strips the pattern and leaves the colour. Colour
25
+ * is deterministic; the pattern is not.
26
+ *
27
+ * With no declared bg capture mode lands on {@link DEFAULT_VARIANT_BG} rather
28
+ * than on the canvas default. `dots` would paint `--prism-bg-surface`, a
29
+ * *theme* token, so the surface behind an undeclared component would depend
30
+ * on whichever theme the runner's browser started in — a baseline that
31
+ * flips with a persisted UI preference. The fixed default is also exactly
32
+ * what the discovery manifest reports for the variant, so what a tool is
33
+ * told and what it screenshots cannot drift apart.
34
+ *
35
+ * Interactive mode keeps `canvas.bg()`: there the dot grid on the themed
36
+ * surface is the point, and forcing `light` would break dark-theme browsing.
37
+ */
10
38
  readonly effective: import("@angular/core").Signal<CanvasBg>;
11
39
  /** True when the user's override differs from the (non-null) recommended bg. */
12
40
  readonly isDeviating: import("@angular/core").Signal<boolean>;
@@ -1 +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"}
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;;AAU/D,qBACa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IACrD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAkC;IAC7D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgC;IACzD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IAEvD,QAAQ,CAAC,WAAW,kDAOjB;IAEH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiC;IAC3D,QAAQ,CAAC,QAAQ,kDAA+B;IAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,QAAQ,CAAC,SAAS,2CAIhB;IAEF,gFAAgF;IAChF,QAAQ,CAAC,WAAW,0CAMjB;;IAUH,WAAW,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI;IAI/B,aAAa,IAAI,IAAI;yCAxEV,qBAAqB;6CAArB,qBAAqB;CA2EjC"}
@@ -1,5 +1,7 @@
1
1
  import { computed, effect, inject, Injectable, signal } from '@angular/core';
2
+ import { declaredVariantBg, DEFAULT_VARIANT_BG, } from '../../shared/variant-bg.js';
2
3
  import { PrismCanvasService } from './prism-canvas.service.js';
4
+ import { PrismCaptureService } from './prism-capture.service.js';
3
5
  import { PrismNavigationService } from './prism-navigation.service.js';
4
6
  import { PrismRendererService } from './prism-renderer.service.js';
5
7
  import * as i0 from "@angular/core";
@@ -7,18 +9,47 @@ export class PrismVariantBgService {
7
9
  canvas = inject(PrismCanvasService);
8
10
  navigation = inject(PrismNavigationService);
9
11
  renderer = inject(PrismRendererService);
12
+ capture = inject(PrismCaptureService);
10
13
  recommended = computed(() => {
11
14
  const comp = this.navigation.activeComponent();
12
15
  if (!comp)
13
16
  return null;
14
- const variant = comp.meta.showcaseConfig.variants?.[this.renderer.activeVariantIndex()];
15
- return variant?.bg ?? comp.meta.showcaseConfig.bg ?? null;
17
+ return declaredVariantBg(comp.meta.showcaseConfig, this.renderer.activeVariantIndex());
16
18
  }, /* @ts-ignore */
17
19
  ...(ngDevMode ? [{ debugName: "recommended" }] : /* istanbul ignore next */ []));
18
20
  _override = signal(null, /* @ts-ignore */
19
21
  ...(ngDevMode ? [{ debugName: "_override" }] : /* istanbul ignore next */ []));
20
22
  override = this._override.asReadonly();
21
- effective = computed(() => this._override() ?? this.recommended() ?? this.canvas.bg(), /* @ts-ignore */
23
+ /**
24
+ * The background the stage actually paints.
25
+ *
26
+ * Capture mode keeps the *declared* background. `@Showcase({ bg })` and a
27
+ * per-variant `bg` exist precisely so a component is judged against the
28
+ * surface it was designed for — an outlined button on `dark` proves nothing
29
+ * when it is screenshotted on the light default — so a capture that dropped
30
+ * them would pin the wrong image.
31
+ *
32
+ * What capture mode does drop is the manual user override: session UI state
33
+ * that must never decide what a baseline looks like. The other half of the
34
+ * determinism problem — the dot grid and the checkerboard, whose phase shifts
35
+ * under a centred component whenever that component changes size — is solved
36
+ * in `CAPTURE_STYLES`, which strips the pattern and leaves the colour. Colour
37
+ * is deterministic; the pattern is not.
38
+ *
39
+ * With no declared bg capture mode lands on {@link DEFAULT_VARIANT_BG} rather
40
+ * than on the canvas default. `dots` would paint `--prism-bg-surface`, a
41
+ * *theme* token, so the surface behind an undeclared component would depend
42
+ * on whichever theme the runner's browser started in — a baseline that
43
+ * flips with a persisted UI preference. The fixed default is also exactly
44
+ * what the discovery manifest reports for the variant, so what a tool is
45
+ * told and what it screenshots cannot drift apart.
46
+ *
47
+ * Interactive mode keeps `canvas.bg()`: there the dot grid on the themed
48
+ * surface is the point, and forcing `light` would break dark-theme browsing.
49
+ */
50
+ effective = computed(() => this.capture.active()
51
+ ? this.recommended() ?? DEFAULT_VARIANT_BG
52
+ : this._override() ?? this.recommended() ?? this.canvas.bg(), /* @ts-ignore */
22
53
  ...(ngDevMode ? [{ debugName: "effective" }] : /* istanbul ignore next */ []));
23
54
  /** True when the user's override differs from the (non-null) recommended bg. */
24
55
  isDeviating = computed(() => {
@@ -1,5 +1,9 @@
1
- export type { NgPrismPlugin, PanelDefinition, ControlDefinition, HeaderWidgetDefinition, NgPrismConfig, PrismManifest, ScannedComponent, InputMeta, OutputMeta, RuntimeManifest, RuntimeComponent, } from './plugin.types.js';
1
+ export type { NgPrismPlugin, PanelDefinition, ControlDefinition, HeaderWidgetDefinition, NgPrismConfig, PrismManifest, ScannedComponent, InputMeta, OutputMeta, RuntimeManifest, RuntimeComponent, DiscoveryManifest, DiscoveryComponent, DiscoveryVariant, DiscoveryPage, } from './plugin.types.js';
2
2
  export type { StyleguidePage, CustomPage, ComponentPage, } from './page.types.js';
3
+ export type { CanvasBg } from '../shared/canvas-bg.type.js';
4
+ export { CANVAS_BGS } from '../shared/canvas-bg.type.js';
5
+ export { DEFAULT_VARIANT_BG, resolveVariantBg } from '../shared/variant-bg.js';
6
+ export type { VariantBgSource } from '../shared/variant-bg.js';
3
7
  export { customPage, componentPage } from './page-helpers.js';
4
8
  export type { ComponentPageOptions } from './page-helpers.js';
5
9
  export { defineConfig } from './define-config.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugin/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACtB,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,UAAU,EACV,eAAe,EACf,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,cAAc,EACd,UAAU,EACV,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC9D,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,YAAY,GACb,MAAM,+BAA+B,CAAC;AACvC,YAAY,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugin/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACtB,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,aAAa,GACd,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,cAAc,EACd,UAAU,EACV,aAAa,GACd,MAAM,iBAAiB,CAAC;AAKzB,YAAY,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC/E,YAAY,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC9D,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,YAAY,GACb,MAAM,+BAA+B,CAAC;AACvC,YAAY,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC"}
@@ -1,3 +1,5 @@
1
+ export { CANVAS_BGS } from '../shared/canvas-bg.type.js';
2
+ export { DEFAULT_VARIANT_BG, resolveVariantBg } from '../shared/variant-bg.js';
1
3
  export { customPage, componentPage } from './page-helpers.js';
2
4
  export { defineConfig } from './define-config.js';
3
5
  export { PRISM_RENDERER_HOOKS, PRISM_MANIFEST, PRISM_CONFIG, } from '../app/tokens/prism-tokens.js';
@@ -1,5 +1,6 @@
1
1
  import type { Provider, Type } from '@angular/core';
2
2
  import type { StyleguidePage } from './page.types.js';
3
+ import type { CanvasBg } from '../shared/canvas-bg.type.js';
3
4
  export interface NgPrismPlugin {
4
5
  /** Unique plugin name — used for debugging and conflict detection */
5
6
  name: string;
@@ -107,6 +108,45 @@ export interface RuntimeComponent {
107
108
  meta: ScannedComponent;
108
109
  type: Type<unknown>;
109
110
  }
111
+ /**
112
+ * The shape external tooling reads from `globalThis.__PRISM_MANIFEST__`.
113
+ *
114
+ * A thin, JSON-safe projection of the runtime manifest: enough to enumerate and
115
+ * address every variant, plus the `@Showcase` metadata a tool needs to decide how
116
+ * to treat one. It carries no Angular class references, and `meta` values that
117
+ * cannot survive a structured clone are dropped rather than half-serialised.
118
+ */
119
+ export interface DiscoveryManifest {
120
+ components: DiscoveryComponent[];
121
+ pages: DiscoveryPage[];
122
+ }
123
+ export interface DiscoveryComponent {
124
+ className: string;
125
+ /** `ShowcaseConfig.title` — the display name, not the class name. */
126
+ title: string;
127
+ variants: DiscoveryVariant[];
128
+ /** Sanitised `ShowcaseConfig.meta`. Omitted when empty. */
129
+ meta?: Record<string, unknown>;
130
+ }
131
+ export interface DiscoveryVariant {
132
+ name: string;
133
+ /** 0-based index into the `variants` array — the `?variant=` URL value. */
134
+ index: number;
135
+ /**
136
+ * The canvas background this variant renders on, already resolved:
137
+ * `Variant.bg`, else `ShowcaseConfig.bg`, else `DEFAULT_VARIANT_BG`.
138
+ *
139
+ * Always present, because a screenshot runner cannot act on "nothing
140
+ * declared" — and it is the background capture mode paints, so the value
141
+ * here and the pixels in the capture agree.
142
+ */
143
+ bg: CanvasBg;
144
+ /** Sanitised `Variant.meta`. Omitted when empty. */
145
+ meta?: Record<string, unknown>;
146
+ }
147
+ export interface DiscoveryPage {
148
+ title: string;
149
+ }
110
150
  export interface NgPrismConfig {
111
151
  plugins?: NgPrismPlugin[];
112
152
  pages?: StyleguidePage[];
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.types.d.ts","sourceRoot":"","sources":["../../src/plugin/plugin.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"plugin.types.d.ts","sourceRoot":"","sources":["../../src/plugin/plugin.types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { CanvasBg } from './canvas-bg.type.js';
2
+ /**
3
+ * The surface a variant is reported to render on when nothing declares one.
4
+ *
5
+ * `checker` is the "no opinion declared" answer: the checkerboard is the
6
+ * universal signal for an undefined or transparent backdrop, it makes an
7
+ * undeclared component visible as such while browsing, and it is exactly how
8
+ * the visual-regression panel already frames a capture whose background it
9
+ * cannot account for.
10
+ *
11
+ * Note what it does *not* buy. `checker` carries no colour of its own — it
12
+ * patterns over `--prism-bg-surface`, a *theme* token — and capture mode
13
+ * strips the pattern and keeps the colour. So the surface behind an undeclared
14
+ * component in a screenshot still follows whichever theme the runner's browser
15
+ * started in. A variant that is under visual regression should declare `light`
16
+ * or `dark`, the two backgrounds whose colour is absolute.
17
+ */
18
+ export declare const DEFAULT_VARIANT_BG: CanvasBg;
19
+ /**
20
+ * The parts of a `ShowcaseConfig` that decide a variant's background.
21
+ *
22
+ * Declared structurally rather than imported: `ShowcaseConfig` itself lives in
23
+ * `decorator/`, which already imports from this folder.
24
+ */
25
+ export interface VariantBgSource {
26
+ bg?: CanvasBg;
27
+ variants?: readonly {
28
+ bg?: CanvasBg;
29
+ }[];
30
+ }
31
+ /**
32
+ * The background a variant *declares*, or `null` when it declares none.
33
+ *
34
+ * Priority is variant, then component. `null` is a meaningful answer — it is
35
+ * what lets the canvas keep the user's own background choice instead of
36
+ * overwriting it, and what makes the background pill able to say "this is your
37
+ * choice, not the component's".
38
+ *
39
+ * An index with no variant behind it still answers with the component's
40
+ * declaration: the renderer clamps an unknown `?variant=` to index 0 rather
41
+ * than rendering nothing, so reporting `null` here would describe a state that
42
+ * cannot occur.
43
+ */
44
+ export declare function declaredVariantBg(config: VariantBgSource, variantIndex: number): CanvasBg | null;
45
+ /**
46
+ * The background a variant renders on, always resolved to a concrete value.
47
+ *
48
+ * This is the answer external tooling needs — a screenshot runner cannot act
49
+ * on "nothing declared" — and the one capture mode paints, so the value
50
+ * reported by `__PRISM_MANIFEST__` and the pixels in the capture agree.
51
+ */
52
+ export declare function resolveVariantBg(config: VariantBgSource, variantIndex: number): CanvasBg;
53
+ //# sourceMappingURL=variant-bg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variant-bg.d.ts","sourceRoot":"","sources":["../../src/shared/variant-bg.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,kBAAkB,EAAE,QAAoB,CAAC;AAEtD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,CAAC,EAAE,QAAQ,CAAC;IACd,QAAQ,CAAC,EAAE,SAAS;QAAE,EAAE,CAAC,EAAE,QAAQ,CAAA;KAAE,EAAE,CAAC;CACzC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,eAAe,EACvB,YAAY,EAAE,MAAM,GACnB,QAAQ,GAAG,IAAI,CAEjB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,eAAe,EACvB,YAAY,EAAE,MAAM,GACnB,QAAQ,CAEV"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The surface a variant is reported to render on when nothing declares one.
3
+ *
4
+ * `checker` is the "no opinion declared" answer: the checkerboard is the
5
+ * universal signal for an undefined or transparent backdrop, it makes an
6
+ * undeclared component visible as such while browsing, and it is exactly how
7
+ * the visual-regression panel already frames a capture whose background it
8
+ * cannot account for.
9
+ *
10
+ * Note what it does *not* buy. `checker` carries no colour of its own — it
11
+ * patterns over `--prism-bg-surface`, a *theme* token — and capture mode
12
+ * strips the pattern and keeps the colour. So the surface behind an undeclared
13
+ * component in a screenshot still follows whichever theme the runner's browser
14
+ * started in. A variant that is under visual regression should declare `light`
15
+ * or `dark`, the two backgrounds whose colour is absolute.
16
+ */
17
+ export const DEFAULT_VARIANT_BG = 'checker';
18
+ /**
19
+ * The background a variant *declares*, or `null` when it declares none.
20
+ *
21
+ * Priority is variant, then component. `null` is a meaningful answer — it is
22
+ * what lets the canvas keep the user's own background choice instead of
23
+ * overwriting it, and what makes the background pill able to say "this is your
24
+ * choice, not the component's".
25
+ *
26
+ * An index with no variant behind it still answers with the component's
27
+ * declaration: the renderer clamps an unknown `?variant=` to index 0 rather
28
+ * than rendering nothing, so reporting `null` here would describe a state that
29
+ * cannot occur.
30
+ */
31
+ export function declaredVariantBg(config, variantIndex) {
32
+ return config.variants?.[variantIndex]?.bg ?? config.bg ?? null;
33
+ }
34
+ /**
35
+ * The background a variant renders on, always resolved to a concrete value.
36
+ *
37
+ * This is the answer external tooling needs — a screenshot runner cannot act
38
+ * on "nothing declared" — and the one capture mode paints, so the value
39
+ * reported by `__PRISM_MANIFEST__` and the pixels in the capture agree.
40
+ */
41
+ export function resolveVariantBg(config, variantIndex) {
42
+ return declaredVariantBg(config, variantIndex) ?? DEFAULT_VARIANT_BG;
43
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-prism/core",
3
- "version": "22.1.3",
3
+ "version": "22.2.0-beta.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",