@ng-prism/core 22.1.4 → 22.2.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -7
- package/dist/app/discovery-manifest.d.ts +13 -0
- package/dist/app/discovery-manifest.d.ts.map +1 -0
- package/dist/app/discovery-manifest.js +106 -0
- package/dist/app/icons/prism-icon.component.d.ts +15 -0
- package/dist/app/icons/prism-icon.component.d.ts.map +1 -1
- package/dist/app/icons/prism-icon.component.js +26 -2
- package/dist/app/provide-prism.d.ts.map +1 -1
- package/dist/app/provide-prism.js +7 -12
- package/dist/app/renderer/overlay-resolver.d.ts +25 -0
- package/dist/app/renderer/overlay-resolver.d.ts.map +1 -0
- package/dist/app/renderer/overlay-resolver.js +25 -0
- package/dist/app/renderer/prism-renderer.component.d.ts +2 -0
- package/dist/app/renderer/prism-renderer.component.d.ts.map +1 -1
- package/dist/app/renderer/prism-renderer.component.js +46 -41
- package/dist/app/services/prism-canvas.service.d.ts +1 -0
- package/dist/app/services/prism-canvas.service.d.ts.map +1 -1
- package/dist/app/services/prism-canvas.service.js +11 -1
- package/dist/app/services/prism-capture.service.d.ts +42 -0
- package/dist/app/services/prism-capture.service.d.ts.map +1 -0
- package/dist/app/services/prism-capture.service.js +100 -0
- package/dist/app/services/prism-persistence.service.d.ts +1 -0
- package/dist/app/services/prism-persistence.service.d.ts.map +1 -1
- package/dist/app/services/prism-persistence.service.js +17 -5
- package/dist/app/services/prism-url-state.service.d.ts.map +1 -1
- package/dist/app/services/prism-url-state.service.js +7 -2
- package/dist/app/services/prism-variant-bg.service.d.ts +28 -0
- package/dist/app/services/prism-variant-bg.service.d.ts.map +1 -1
- package/dist/app/services/prism-variant-bg.service.js +34 -3
- package/dist/plugin/index.d.ts +5 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +2 -0
- package/dist/plugin/plugin.types.d.ts +40 -0
- package/dist/plugin/plugin.types.d.ts.map +1 -1
- package/dist/shared/variant-bg.d.ts +53 -0
- package/dist/shared/variant-bg.d.ts.map +1 -0
- package/dist/shared/variant-bg.js +43 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -135,13 +135,14 @@ Link to a `@Showcase`-decorated component for combined API docs + custom renderi
|
|
|
135
135
|
|
|
136
136
|
## Official Plugins
|
|
137
137
|
|
|
138
|
-
| Plugin | Package
|
|
139
|
-
| --------- |
|
|
140
|
-
| JSDoc | `@ng-prism/plugin-jsdoc`
|
|
141
|
-
| Figma | `@ng-prism/plugin-figma`
|
|
142
|
-
| Box Model | `@ng-prism/plugin-box-model`
|
|
143
|
-
| Perf | `@ng-prism/plugin-perf`
|
|
144
|
-
| Coverage | `@ng-prism/plugin-coverage`
|
|
138
|
+
| Plugin | Package | Description |
|
|
139
|
+
| --------- | ------------------------------------ | ------------------------------------------------------------- |
|
|
140
|
+
| JSDoc | `@ng-prism/plugin-jsdoc` | API documentation from JSDoc comments |
|
|
141
|
+
| Figma | `@ng-prism/plugin-figma` | Figma design embed + visual diff |
|
|
142
|
+
| Box Model | `@ng-prism/plugin-box-model` | CSS box model inspector |
|
|
143
|
+
| Perf | `@ng-prism/plugin-perf` | Render performance profiling |
|
|
144
|
+
| Coverage | `@ng-prism/plugin-coverage` | Per-component test coverage from Istanbul/v8 |
|
|
145
|
+
| VRT | `@ng-prism/plugin-visual-regression` | Per-variant visual regression report from a screenshot runner |
|
|
145
146
|
|
|
146
147
|
> **Note:** Accessibility auditing (axe-core) is built into ng-prism core — no plugin needed.
|
|
147
148
|
|
|
@@ -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":";
|
|
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 =
|
|
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:
|
|
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;
|
|
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
|
-
//
|
|
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
|
-
|
|
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;;
|
|
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
|
|
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
|
|
22
|
-
i0.ɵɵtemplate(0,
|
|
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
|
|
127
|
-
|
|
128
|
-
this.
|
|
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
|
-
|
|
132
|
-
|
|
149
|
+
this.activeOverlay.set(null);
|
|
150
|
+
if (resolution.kind !== 'lazy')
|
|
133
151
|
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
if (
|
|
138
|
-
this.activeOverlay.set(
|
|
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
|
-
|
|
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:
|
|
234
|
-
i0.ɵɵelementStart(0, "div", 1)
|
|
235
|
-
i0.ɵɵ
|
|
236
|
-
i0.ɵɵ
|
|
237
|
-
i0.ɵɵ
|
|
238
|
-
i0.ɵɵ
|
|
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.ɵɵ
|
|
248
|
-
i0.ɵɵadvance(
|
|
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() ?
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
@@ -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;;
|
|
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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prism-persistence.service.d.ts","sourceRoot":"","sources":["../../../src/app/services/prism-persistence.service.ts"],"names":[],"mappings":";
|
|
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' ||
|
|
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 &&
|
|
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] = {
|
|
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;
|
|
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
|
|
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
|
|
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;;
|
|
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
|
-
|
|
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
|
-
|
|
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(() => {
|
package/dist/plugin/index.d.ts
CHANGED
|
@@ -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,
|
|
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"}
|
package/dist/plugin/index.js
CHANGED
|
@@ -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":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;
|
|
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