@pygmalionjs/pygmalion 0.5.31 → 0.5.33

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.
@@ -1,4 +1,4 @@
1
- import { F as s, N as a, e as r, s as i, a as t } from "./FrozenRoutePreview-B6E3mX9a.js";
1
+ import { F as s, N as a, e as r, s as i, a as t } from "./FrozenRoutePreview-hofdx4eP.js";
2
2
  export {
3
3
  s as FrozenRoutePreviewView,
4
4
  a as NodeModel,
@@ -2,6 +2,25 @@ import type { InstanceHit, InstanceResolver } from './fiberMap';
2
2
  import type { NodeJSON } from './store';
3
3
  /** Keeps a real 0 — the value that hides hover-only affordances. */
4
4
  export declare function clampOpacity(value: string): number;
5
+ /**
6
+ * Where an imported layer sat in the source document, in model px relative to
7
+ * the imported root. The walk reads every element's client rect anyway; keeping
8
+ * the boxes lets the fidelity check (importFidelity.ts) re-measure the mounted
9
+ * layer tree and compare — a reconstruction is only trustworthy when it puts
10
+ * the same boxes in the same places the capture had them.
11
+ *
12
+ * Entries are addressed by child-index path, not node id: the store re-issues
13
+ * ids when it deserializes an imported tree, while the tree structure survives
14
+ * unchanged.
15
+ */
16
+ export interface ImportedGeometryEntry {
17
+ /** Child-index chain from the imported root ([] is the root itself). */
18
+ path: readonly number[];
19
+ x: number;
20
+ y: number;
21
+ width: number;
22
+ height: number;
23
+ }
5
24
  export interface DomImportDiagnostic {
6
25
  severity: 'warning';
7
26
  stage: 'capture';
@@ -51,6 +70,8 @@ export interface DomImportResult {
51
70
  truncated: boolean;
52
71
  diagnostics: readonly DomImportDiagnostic[];
53
72
  metrics: DomImportMetrics;
73
+ /** Source boxes of the imported layers, for the post-mount fidelity check. */
74
+ geometry: readonly ImportedGeometryEntry[];
54
75
  }
55
76
  /** Transformation entry point starting from Element in the same document (or iframe document) — win is the window to which el belongs. */
56
77
  export declare function importDomFromElement(el: Element, win: Window, opts?: DomImportOptions): DomImportResult | null;
@@ -0,0 +1,85 @@
1
+ import type { ImportedGeometryEntry } from './domImport';
2
+ /** The shape of a mounted layer tree this check needs — NodeModel satisfies it. */
3
+ export interface LayerImportFidelityNode {
4
+ id: string;
5
+ name?: string;
6
+ hidden?: boolean;
7
+ children?: readonly LayerImportFidelityNode[];
8
+ }
9
+ interface MeasuredBox {
10
+ x: number;
11
+ y: number;
12
+ width: number;
13
+ height: number;
14
+ }
15
+ export interface MeasurableElement {
16
+ getBoundingClientRect(): MeasuredBox;
17
+ ownerDocument?: {
18
+ createRange?: () => {
19
+ selectNodeContents(node: unknown): void;
20
+ getBoundingClientRect(): MeasuredBox;
21
+ };
22
+ };
23
+ }
24
+ export interface LayerImportBox {
25
+ x: number;
26
+ y: number;
27
+ width: number;
28
+ height: number;
29
+ }
30
+ export interface LayerImportDrift {
31
+ nodeId: string;
32
+ name: string;
33
+ path: readonly number[];
34
+ /** Where the capture had the layer, model px relative to the root. */
35
+ source: LayerImportBox;
36
+ /** Where the reconstruction put it. */
37
+ rendered: LayerImportBox;
38
+ dx: number;
39
+ dy: number;
40
+ dwidth: number;
41
+ dheight: number;
42
+ /** Largest absolute component of the drift, in model px. */
43
+ drift: number;
44
+ }
45
+ export interface LayerImportFidelityReport {
46
+ /** Layers that had both a source box and a mounted element to measure. */
47
+ checked: number;
48
+ /** Layers whose box moved or resized beyond the tolerance. */
49
+ drifted: number;
50
+ maxDriftPx: number;
51
+ /** The largest offenders, capped, largest first. */
52
+ worst: readonly LayerImportDrift[];
53
+ /** False when the reconstruction should not be trusted as the screen. */
54
+ ok: boolean;
55
+ }
56
+ /**
57
+ * Sub-tolerance drift is expected, not suspicious: fit/fill sizing modes
58
+ * quantize to whole px, text metrics round per line, and the canvas transform
59
+ * divides back out through floating point. 4px keeps that noise out of the
60
+ * report while catching any real layout change — one dropped wrap moves a
61
+ * box by a line height.
62
+ */
63
+ export declare const LAYER_DRIFT_TOLERANCE_PX = 4;
64
+ /** A single layer this far off means the screen no longer reads as captured. */
65
+ export declare const LAYER_FIDELITY_MAX_DRIFT_PX = 12;
66
+ /** Many slightly-off layers are as untrustworthy as one badly-off layer. */
67
+ export declare const LAYER_FIDELITY_MAX_DRIFT_RATIO = 0.05;
68
+ /** Resolves a geometry path (child-index chain) inside a mounted layer tree. */
69
+ export declare function nodeAtGeometryPath(root: LayerImportFidelityNode, path: readonly number[]): LayerImportFidelityNode | null;
70
+ export interface LayerImportFidelityInput {
71
+ root: LayerImportFidelityNode;
72
+ geometry: readonly ImportedGeometryEntry[];
73
+ /** Canvas zoom at measurement time — rendered rects are visual px. */
74
+ scale: number;
75
+ /** data-node-id lookup, injectable so the comparison is testable. */
76
+ resolveElement: (nodeId: string) => MeasurableElement | null;
77
+ }
78
+ /**
79
+ * Measures the mounted layer tree against the source geometry the import
80
+ * recorded. Returns null when the tree is not measurable yet (the root element
81
+ * is unmounted, or nothing could be paired) — callers retry after mount; a
82
+ * report, once produced, is final for that import.
83
+ */
84
+ export declare function assessLayerImportFidelity(input: LayerImportFidelityInput): LayerImportFidelityReport | null;
85
+ export {};
@@ -4,6 +4,7 @@ import { type CardBadgeOption, type CardSlotDef, type ScreenCardDef } from './sc
4
4
  import { type ScreenListDef } from './screenLists';
5
5
  import { type DesignImportAsset, type DesignImportKind, type DesignFrameSizeMode, type DesignScreenAssertion, type DesignScreenInteraction, type DesignScreenPreset, type StoryboardEnvironment } from './designImport';
6
6
  import { type DomImportDiagnostic, type DomImportResult } from './domImport';
7
+ import { type LayerImportFidelityReport, type MeasurableElement } from './importFidelity';
7
8
  import { type DesignFrameHeightMode } from './frameHeight';
8
9
  import { type RegistryPropValue } from './registry';
9
10
  import { type PrimitiveSourceValue } from './inspect';
@@ -231,6 +232,8 @@ export interface PageModel {
231
232
  layerImportTruncated?: boolean;
232
233
  layerImportDiagnostics?: readonly DomImportDiagnostic[];
233
234
  layerImportError?: string;
235
+ /** Post-mount geometry comparison of the imported layers vs their capture. */
236
+ layerImportFidelity?: LayerImportFidelityReport;
234
237
  /** Page edit revision recorded when the layers were imported. */
235
238
  layerImportRevision?: number;
236
239
  /** Frame width the layers were imported at — their geometry is only valid there. */
@@ -425,6 +428,8 @@ export declare class EditorStore {
425
428
  private pageEditRevisions;
426
429
  /** pageId → edit revision at the moment a frozen preview was promoted without a boot (D2). */
427
430
  private provisionalLayerImports;
431
+ /** pageId → source geometry awaiting its post-mount fidelity measurement. */
432
+ private pendingLayerImportGeometry;
428
433
  private contentLayoutScheduled;
429
434
  private contentLayoutCanvases;
430
435
  constructor();
@@ -680,8 +685,22 @@ export declare class EditorStore {
680
685
  /** Keeps the current (user-edited) tree and stops further live re-import attempts for the page. */
681
686
  discardProvisionalLayerImport(pageId: string): void;
682
687
  /** Design importer automatically inserts the capture results into the existing frame. The route is maintained for live preview. */
683
- replacePageRootFromDom(pageId: string, rootJson: NodeJSON, result?: Pick<DomImportResult, 'count' | 'truncated'> & Partial<Pick<DomImportResult, 'diagnostics' | 'metrics'>>): void;
688
+ replacePageRootFromDom(pageId: string, rootJson: NodeJSON, result?: Pick<DomImportResult, 'count' | 'truncated'> & Partial<Pick<DomImportResult, 'diagnostics' | 'metrics' | 'geometry'>>): void;
684
689
  setPageLayerImportError(pageId: string, message: string): void;
690
+ /**
691
+ * Runs the fidelity measurement once the imported tree has committed and
692
+ * laid out — one frame for React, one for layout. FrameView re-invokes the
693
+ * assessment on mount as well, because a frame can import while its canvas
694
+ * surface is detached (LOD) and only become measurable later.
695
+ */
696
+ private scheduleLayerImportFidelityCheck;
697
+ /**
698
+ * Compares the mounted layer tree against the source geometry its import
699
+ * recorded (importFidelity.ts). Idempotent per import: the pending geometry
700
+ * is consumed by the first measurement that finds the tree mounted, and a
701
+ * re-import queues fresh geometry.
702
+ */
703
+ assessLayerImportFidelityForPage(pageId: string, resolveElement?: (nodeId: string) => MeasurableElement | null): void;
685
704
  /** Switch pages by name — Path used by the host fixture's actual button handler (pygmalionNavigate). */
686
705
  navigateToPageByName(name: string): void;
687
706
  /** Show preview overlay — page navigation with Scyllender, linkTo/host navigation without editor chrome. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pygmalionjs/pygmalion",
3
- "version": "0.5.31",
3
+ "version": "0.5.33",
4
4
  "description": "Code-backed DOM design sandbox and visual QA editor",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {