@pygmalionjs/pygmalion 0.6.28 → 0.6.30

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-Xm7o3urC.js";
1
+ import { F as s, N as a, e as r, s as i, a as t } from "./FrozenRoutePreview-D9J35QsM.js";
2
2
  export {
3
3
  s as FrozenRoutePreviewView,
4
4
  a as NodeModel,
@@ -1,5 +1,8 @@
1
+ import type { DesignCodegenProjectManifest } from './editor/designCompiler';
1
2
  export interface AppProps {
2
3
  /** The route identity is stable enough to read or produce preview artifacts. */
3
4
  previewIdentityReady?: boolean;
5
+ /** Checked-in host contract used by repository-aware codegen adapters. */
6
+ codegenProject?: DesignCodegenProjectManifest;
4
7
  }
5
8
  export declare const App: import("react").FunctionComponent<AppProps>;
@@ -17,4 +17,7 @@ export declare const ShadowRoutePreview: import("react").FunctionComponent<{
17
17
  mountPriority?: number;
18
18
  onDocumentHeightChange?: (height: number) => void;
19
19
  onReplayIssuesChange?: (issues: string[]) => void;
20
+ onReachableTestIdsChange?: (testIds: readonly string[]) => void;
21
+ /** Enables immediate scrolling while the live React document warms. */
22
+ interactionEnabled?: boolean;
20
23
  }>;
@@ -2,6 +2,8 @@ import { type CodegenFrameOptions, type CodegenNode } from './codegen';
2
2
  import { type NodeJSON } from './store';
3
3
  export declare const DESIGN_IR_SCHEMA: "pygmalion.design-ir";
4
4
  export declare const DESIGN_IR_VERSION: 1;
5
+ export declare const DESIGN_CODEGEN_PROJECT_SCHEMA: "pygmalion.codegen-project";
6
+ export declare const DESIGN_CODEGEN_PROJECT_VERSION: 1;
5
7
  export type DesignCompilePurpose = 'interactive' | 'export';
6
8
  export type DesignCompileSeverity = 'info' | 'warning' | 'error';
7
9
  export type DesignCompileStage = 'normalize' | 'analyze' | 'generate' | 'resource';
@@ -43,6 +45,70 @@ export interface DesignIRMetrics {
43
45
  componentCount: number;
44
46
  assetCount: number;
45
47
  }
48
+ /** JSON-shaped host data produced from a repository-owned codegen contract. */
49
+ export type DesignCodegenData = null | string | number | boolean | readonly DesignCodegenData[] | {
50
+ readonly [key: string]: DesignCodegenData;
51
+ };
52
+ export interface DesignCodegenPromptReference {
53
+ /** Repository-relative agent instruction file used to author this manifest. */
54
+ sourcePath: string;
55
+ /** Host-owned revision bumped when the instructions or output contract change. */
56
+ revision: string;
57
+ }
58
+ export interface DesignCodegenFrameContract {
59
+ /** Canonical source anchors chosen by the host, in addition to observed files. */
60
+ sourcePaths?: readonly string[];
61
+ /** Adapter-specific, deterministic metadata. It is never interpreted by core. */
62
+ metadata?: Readonly<Record<string, DesignCodegenData>>;
63
+ }
64
+ /**
65
+ * Checked-in output of a repository's codegen instructions.
66
+ *
67
+ * The prompt is provenance, not a runtime dependency: adapters consume this
68
+ * manifest synchronously and never call a model while the editor is open.
69
+ */
70
+ export interface DesignCodegenProjectManifest {
71
+ schema: typeof DESIGN_CODEGEN_PROJECT_SCHEMA;
72
+ version: typeof DESIGN_CODEGEN_PROJECT_VERSION;
73
+ defaultTarget?: string;
74
+ prompt?: DesignCodegenPromptReference;
75
+ frames?: Readonly<Record<string, DesignCodegenFrameContract>>;
76
+ }
77
+ export interface DesignCodegenPageContext {
78
+ /** Canonical import id when available, otherwise the editor-local page id. */
79
+ id: string;
80
+ name: string;
81
+ route?: string;
82
+ previewRoute?: string;
83
+ sourcePaths: readonly string[];
84
+ flow?: string;
85
+ scenario?: string;
86
+ state?: string;
87
+ stateKind?: string;
88
+ scenarioId?: string;
89
+ scenarioIds: readonly string[];
90
+ desiredState?: Readonly<Record<string, DesignCodegenData>>;
91
+ /** Current reproducible recipe; direct state stays separate for easy adapters. */
92
+ capture: {
93
+ environment?: DesignCodegenData;
94
+ preset?: DesignCodegenData;
95
+ interactions: readonly DesignCodegenData[];
96
+ assertions: readonly DesignCodegenData[];
97
+ };
98
+ interactiveStateId?: string;
99
+ interactiveOptionId?: string;
100
+ interactiveOptionIds?: readonly string[];
101
+ viewport: {
102
+ width: number;
103
+ height: number;
104
+ };
105
+ /** Frame-specific checked-in contract selected by id. */
106
+ contract?: DesignCodegenFrameContract;
107
+ }
108
+ export interface DesignCodegenRuntimeContext {
109
+ page?: DesignCodegenPageContext;
110
+ project?: DesignCodegenProjectManifest;
111
+ }
46
112
  export interface DesignIntermediateDocumentV1 {
47
113
  schema: typeof DESIGN_IR_SCHEMA;
48
114
  version: typeof DESIGN_IR_VERSION;
@@ -61,7 +127,7 @@ export interface DesignCompileLimits {
61
127
  maxOutputBytes: number;
62
128
  }
63
129
  export declare const DEFAULT_DESIGN_COMPILE_LIMITS: Readonly<DesignCompileLimits>;
64
- export interface DesignCodegenAdapterContext {
130
+ export interface DesignCodegenAdapterContext extends DesignCodegenRuntimeContext {
65
131
  document: DesignIntermediateDocumentV1;
66
132
  }
67
133
  export interface DesignCodegenAdapterOutput {
@@ -73,6 +139,13 @@ export interface DesignCodegenAdapter {
73
139
  label: string;
74
140
  language: string;
75
141
  extension: string;
142
+ /**
143
+ * Repository adapters generate from checked-in source contracts rather than
144
+ * approximating the captured design tree. They still receive the document
145
+ * for source-map context, but design-IR warnings and preview size deferral do
146
+ * not describe their output.
147
+ */
148
+ basis?: 'design-ir' | 'repository';
76
149
  generate(context: DesignCodegenAdapterContext): string | DesignCodegenAdapterOutput;
77
150
  }
78
151
  export interface DesignCompileOptions {
@@ -81,6 +154,8 @@ export interface DesignCompileOptions {
81
154
  force?: boolean;
82
155
  frame?: CodegenFrameOptions;
83
156
  limits?: Partial<DesignCompileLimits>;
157
+ /** Current repository frame and checked-in codegen contract for host adapters. */
158
+ context?: DesignCodegenRuntimeContext;
84
159
  }
85
160
  export interface DesignCompileResult {
86
161
  target: string;
@@ -144,7 +144,9 @@ export declare const __flowSessionWarmDebug: {
144
144
  bootsByRunner: Record<string, number>;
145
145
  };
146
146
  /** Drops parked instances globally, or only those owned by one runner. */
147
- export declare function evictFlowSessionWarmInstances(ownerLabel?: string): void;
147
+ export declare function evictFlowSessionWarmInstances(ownerLabel?: string, options?: {
148
+ preservePinned?: boolean;
149
+ }): void;
148
150
  /**
149
151
  * True when a walk of this path would resume on a parked instance. Activation
150
152
  * warming reads it to decide whether the frame already has a resumable
@@ -1,4 +1,4 @@
1
- export type FrameInteractionPhase = 'idle' | 'starting' | 'interacting';
1
+ export type FrameInteractionPhase = 'idle' | 'starting' | 'preview' | 'interacting';
2
2
  export interface FrameInteractionScrollState {
3
3
  x: number;
4
4
  y: number;
@@ -51,6 +51,12 @@ export declare function subscribeFrameInteraction(listener: () => void): () => v
51
51
  * without learning whether the running surface is an iframe or another host.
52
52
  */
53
53
  export declare function connectFrameInteractionViewport(frameId: string, adapter: FrameInteractionViewportAdapter): () => void;
54
+ /**
55
+ * Makes the frozen DOM useful immediately while its live React document warms.
56
+ * The preview is a scroll-only bridge: navigation and form submission stay
57
+ * contained, and the live iframe later replaces it without ending the session.
58
+ */
59
+ export declare function connectFrameInteractionPreview(frameId: string, root: ShadowRoot, contentElement: HTMLElement): () => void;
54
60
  /** Scrolls the running frame viewport and immediately republishes its reading. */
55
61
  export declare function setFrameInteractionScroll(position: {
56
62
  x?: number;
@@ -6,6 +6,7 @@
6
6
  * published, while the active frame may mirror the running document
7
7
  * immediately so capture stability never becomes interaction latency.
8
8
  */
9
+ import type { DesignScreenDesiredState } from './designImport';
9
10
  export interface InteractiveSessionSurface {
10
11
  iframe: HTMLIFrameElement;
11
12
  screenId: string;
@@ -19,8 +20,26 @@ export declare function directInteractiveSessionSurfaceId(pageId: string, stateI
19
20
  export declare function setDirectInteractiveStateRequestHandler(handler: ((pageId: string) => void) | null): void;
20
21
  /** Starts direct-state delivery in the same task as the right-panel click. */
21
22
  export declare function requestDirectInteractiveState(pageId: string): boolean;
23
+ /** Installs the paint-first path used before the observable page recipe changes. */
24
+ export declare function setDirectInteractiveStatePreviewHandler(handler: ((request: {
25
+ pageId: string;
26
+ stateId: string;
27
+ desiredState: DesignScreenDesiredState;
28
+ }) => boolean) | null): void;
29
+ /** Applies a direct host state without invalidating the editor model yet. */
30
+ export declare function requestDirectInteractiveStatePreview(request: {
31
+ pageId: string;
32
+ stateId: string;
33
+ desiredState: DesignScreenDesiredState;
34
+ }): boolean;
22
35
  /** Exposes a runner-owned iframe without transferring its ownership. */
23
36
  export declare function exposeInteractiveSessionSurface(screenId: string, iframe: HTMLIFrameElement): void;
37
+ /**
38
+ * Hands an in-flight prewarm slot to the frame that has just exposed its axis.
39
+ * If preparation has not presented the iframe yet, its first exposure follows
40
+ * the redirect instead of landing in the now-private staging slot.
41
+ */
42
+ export declare function redirectInteractiveSessionSurface(fromScreenId: string, toScreenId: string): boolean;
24
43
  /** Releases a surface only when the caller still owns the registered iframe. */
25
44
  export declare function releaseInteractiveSessionSurface(screenId: string, iframe: HTMLIFrameElement): void;
26
45
  /** Clears a presentation slot when its page returns to an authored state. */
@@ -1,5 +1,6 @@
1
1
  import type { DesignScreenInteraction, DesignSerializable, StoryboardEnvironment } from './designImport';
2
2
  import type { PageModel } from './store';
3
+ export { reachablePreviewTestIds } from './previewReachability';
3
4
  /**
4
5
  * Screen-state axes — same-frame variations reproduced by a gesture or a boot
5
6
  * condition.
@@ -136,10 +137,10 @@ export declare function validateInteractiveStates(defs: readonly InteractiveStat
136
137
  /** Validates both interaction and condition axes. */
137
138
  export declare const validateScreenStateAxes: typeof validateInteractiveStates;
138
139
  /** Reads only the control-presence evidence needed by the view-only inspector. */
139
- export declare function previewMarkupTestIds(markup: string | null | undefined): readonly string[];
140
+ export declare function previewMarkupTestIds(markup: string | null | undefined, bodyAttributes?: string | null): readonly string[];
140
141
  /**
141
142
  * Axes available on one frame. The test-id requirement is checked against
142
- * either the imported tree or the frozen preview's lightweight test-id index.
143
+ * either the imported tree or the frozen preview's reachable test-id index.
143
144
  * View-only can therefore offer a real screen control without importing the
144
145
  * whole layer tree, while a frame without that control still hides the axis.
145
146
  */
@@ -0,0 +1,7 @@
1
+ export declare const REACHABLE_TEST_IDS_ATTRIBUTE = "data-pygmalion-reachable-testids";
2
+ /** Test ids whose rendered controls can actually be reached from this frame. */
3
+ export declare function reachablePreviewTestIds(root: ParentNode): readonly string[];
4
+ /** Stamps capture-time reachability without mutating the running document. */
5
+ export declare function stampReachablePreviewTestIds(source: Document, clone: HTMLElement): void;
6
+ /** Reads a capture-time reachability index, or null for a legacy snapshot. */
7
+ export declare function indexedReachablePreviewTestIds(markup: string): readonly string[] | null;
@@ -221,6 +221,12 @@ export interface PageModel {
221
221
  interactiveStateId?: string;
222
222
  /** The active option is rendered through host desired state, without replay. */
223
223
  interactiveStateUsesDesiredState?: boolean;
224
+ /**
225
+ * Imported layers still showing behind a direct live surface. The surface
226
+ * paints first; FrameView consumes this key and invalidates the old layers
227
+ * on the following frame so a large tree cannot block the visible switch.
228
+ */
229
+ interactiveLayerRefreshKey?: string;
224
230
  interactiveOptionId?: string;
225
231
  /**
226
232
  * Options held together on a combining axis (InteractiveStateDef.multiple).
@@ -260,7 +266,7 @@ export interface PageModel {
260
266
  assertions?: DesignScreenAssertion[];
261
267
  /** Host-declared warm instance group. Set when the screen state is declared, not replayed. */
262
268
  session?: string;
263
- /** Test ids observed in the frozen preview before editable layers are imported. */
269
+ /** Reachable test ids observed in the frozen preview before editable layers are imported. */
264
270
  previewTestIds?: readonly string[];
265
271
  layerImportCount?: number;
266
272
  layerImportTruncated?: boolean;
@@ -467,6 +473,8 @@ export declare class EditorStore {
467
473
  private pageEditRevisions;
468
474
  /** pageId → edit revision at the moment a frozen preview was promoted without a boot (D2). */
469
475
  private provisionalLayerImports;
476
+ /** Latest paint-first direct-state commit scheduled for each page. */
477
+ private directStateCommitRevisions;
470
478
  /** pageId → source geometry awaiting its post-mount fidelity measurement. */
471
479
  private pendingLayerImportGeometry;
472
480
  /** View-only motion playback never becomes part of a frame recipe or undo history. */
@@ -737,7 +745,11 @@ export declare class EditorStore {
737
745
  * Pure-CSS pseudo options short-circuit through the held-pseudo fast path
738
746
  * (applyHeldPseudoInteractiveState) and never touch the recipe.
739
747
  */
740
- applyInteractiveState(pageId: string, def: InteractiveStateDef, optionId: string): boolean;
748
+ applyInteractiveState(pageId: string, def: InteractiveStateDef, optionId: string, options?: {
749
+ skipPaintFirstPreview?: boolean;
750
+ }): boolean;
751
+ /** Invalidates retained layers only after their direct live replacement painted. */
752
+ commitInteractiveLayerRefresh(pageId: string, refreshKey: string): boolean;
741
753
  /** The root viewport endpoint currently preserved in this frame recipe. */
742
754
  frameScrollRecipe(pageId: string): Readonly<{
743
755
  x: number;
@@ -18,6 +18,7 @@ import type { StoryboardDefinition } from './editor/storyboardGraph';
18
18
  import { type TokenDef } from './editor/tokens';
19
19
  import { type ViewportPresetDef } from './editor/viewportPresets';
20
20
  import { type PreviewEnvironmentControlDef } from './editor/previewEnvironmentControls';
21
+ import type { DesignCodegenProjectManifest } from './editor/designCompiler';
21
22
 
22
23
  export { setComponentRegistry } from './editor/registry';
23
24
  export { PYGMALION_PREVIEW_CAPTURE_PROGRESS_SUFFIX, previewCaptureProgressEndpoint, usePygmalionPreviewCaptureProgress, } from './editor/previewCaptureProgress';
@@ -27,8 +28,8 @@ export type { FrameInteractionPhase, FrameInteractionScrollState, FrameInteracti
27
28
  export type { ComponentRegistry, RegistryEntry, RegistryPropValue, } from './editor/registry';
28
29
  export { enumerateDesignComponentConnectionCases, getDesignComponentConnection, getDesignComponentConnectionForComponent, getDesignComponentConnections, setDesignComponentConnections, validateDesignComponentConnections, } from './editor/componentConnections';
29
30
  export type { DesignComponentConnectionCase, DesignComponentConnectionCases, DesignComponentConnectionDef, DesignComponentReference, DesignConnectionDiagnostic, DesignConnectionPropertyDef, DesignConnectionPropertyKind, } from './editor/componentConnections';
30
- export { compileDesignDocument, createDesignIntermediateDocument, DEFAULT_DESIGN_COMPILE_LIMITS, DESIGN_IR_SCHEMA, DESIGN_IR_VERSION, getDesignCodegenAdapters, registerDesignCodegenAdapter, } from './editor/designCompiler';
31
- export type { DesignCodegenAdapter, DesignCodegenAdapterContext, DesignCodegenAdapterOutput, DesignCompileDiagnostic, DesignCompileLimits, DesignCompileOptions, DesignCompilePurpose, DesignCompileResult, DesignCompileSeverity, DesignCompileStage, DesignCompileStatus, DesignIntermediateDocumentV1, DesignIRAsset, DesignIRComponent, DesignIRMetrics, } from './editor/designCompiler';
31
+ export { compileDesignDocument, createDesignIntermediateDocument, DEFAULT_DESIGN_COMPILE_LIMITS, DESIGN_CODEGEN_PROJECT_SCHEMA, DESIGN_CODEGEN_PROJECT_VERSION, DESIGN_IR_SCHEMA, DESIGN_IR_VERSION, getDesignCodegenAdapters, registerDesignCodegenAdapter, } from './editor/designCompiler';
32
+ export type { DesignCodegenAdapter, DesignCodegenAdapterContext, DesignCodegenAdapterOutput, DesignCodegenData, DesignCodegenFrameContract, DesignCodegenPageContext, DesignCodegenProjectManifest, DesignCodegenPromptReference, DesignCodegenRuntimeContext, DesignCompileDiagnostic, DesignCompileLimits, DesignCompileOptions, DesignCompilePurpose, DesignCompileResult, DesignCompileSeverity, DesignCompileStage, DesignCompileStatus, DesignIntermediateDocumentV1, DesignIRAsset, DesignIRComponent, DesignIRMetrics, } from './editor/designCompiler';
32
33
  export type { CodegenFrameOptions } from './editor/codegen';
33
34
  export { createDesignComponentCatalog, createDesignComponentVariantMatrix, createDesignGalleryCatalog, designCatalogKey, designCatalogRegistryName, PygmalionCatalogDocument, PygmalionCatalogMatrix, PygmalionCatalogSection, } from './editor/catalog';
34
35
  export type { DesignCatalogBuild, DesignCatalogFrame, DesignCatalogItem, DesignCatalogKind, DesignCatalogPageDescriptor, DesignComponentCatalogOptions, DesignComponentVariantMatrix, DesignGalleryCatalogOptions, PygmalionCatalogDocumentProps, PygmalionCatalogMatrixProps, PygmalionCatalogSectionProps, } from './editor/catalog';
@@ -281,7 +282,7 @@ export declare function pygmalionOpenPreview(): void;
281
282
  export declare function pygmalionResetCurrentEdits(): boolean;
282
283
  export interface InitialPageDef extends DesignImportInitialPage {
283
284
  }
284
- export declare function PygmalionEditor({ registry, tokens, initialPages, initialCanvas, initialEditMode, componentConnections, viewportPresets, sessionPresets, previewEnvironmentControls, designImport, onTokensChange, appOrigin, previewRevision, previewCacheNamespace, previewArtifacts, previewArtifactEndpoint, previewConcurrency, previewOpen, onPreviewOpenChange, flowCanvas, frameSurface, captureSupply, screenFlows, screenFlowConcurrency, scenarioCoverage, screenDimensions, screenLists, sectionHeaders, sectionHeaderLabels, screenLanes, frameBranchKinds, frameLabelLabels, screenCards, liveScreenBudget, screenStateAxes, interactiveStates, surfaceClassifications, onApply, onDesignChange, onInspectApply, onInspectPreview, onInspectImpact, storyboard, storyboardScenarios, sessionStoryboards, storyboardDiscovery, storyboardDiscoveryEndpoint, }: {
285
+ export declare function PygmalionEditor({ registry, tokens, initialPages, initialCanvas, initialEditMode, componentConnections, codegenProject, viewportPresets, sessionPresets, previewEnvironmentControls, designImport, onTokensChange, appOrigin, previewRevision, previewCacheNamespace, previewArtifacts, previewArtifactEndpoint, previewConcurrency, previewOpen, onPreviewOpenChange, flowCanvas, frameSurface, captureSupply, screenFlows, screenFlowConcurrency, scenarioCoverage, screenDimensions, screenLists, sectionHeaders, sectionHeaderLabels, screenLanes, frameBranchKinds, frameLabelLabels, screenCards, liveScreenBudget, screenStateAxes, interactiveStates, surfaceClassifications, onApply, onDesignChange, onInspectApply, onInspectPreview, onInspectImpact, storyboard, storyboardScenarios, sessionStoryboards, storyboardDiscovery, storyboardDiscoveryEndpoint, }: {
285
286
  registry?: ComponentRegistry;
286
287
  tokens?: TokenDef[];
287
288
  initialPages?: InitialPageDef[];
@@ -296,6 +297,11 @@ export declare function PygmalionEditor({ registry, tokens, initialPages, initia
296
297
  initialEditMode?: 'edit' | 'view-only';
297
298
  /** Explicit external-design to registered-code component mappings. */
298
299
  componentConnections?: readonly DesignComponentConnectionDef[];
300
+ /**
301
+ * Checked-in repository contract for deterministic, state-aware codegen.
302
+ * Its prompt reference is provenance only; no model runs in the editor.
303
+ */
304
+ codegenProject?: DesignCodegenProjectManifest;
299
305
  /** Named frame widths the application supports — rendered as one-click buttons beside the W/H boxes. */
300
306
  viewportPresets?: readonly ViewportPresetDef[];
301
307
  /**
@@ -1 +1,4 @@
1
- export declare const CodePanel: import("react").FunctionComponent<object>;
1
+ import { type DesignCodegenProjectManifest } from '../editor/designCompiler';
2
+ export declare const CodePanel: import("react").FunctionComponent<{
3
+ codegenProject?: DesignCodegenProjectManifest;
4
+ }>;
@@ -75,10 +75,13 @@ automatic duplicate. Click-driven toggles and other application states remain
75
75
  declared axes unless they already have their own review frame.
76
76
 
77
77
  In view-only mode, `requires.testId` is matched against the frozen preview's
78
- lightweight test-id index as well as an imported layer tree. This keeps right
79
- panel state controls available without turning selection into a layer import or
80
- live boot, and still hides an axis from screens whose captured DOM lacks its
81
- target.
78
+ lightweight **reachable** test-id index as well as an imported layer tree. A
79
+ test id under `display: none`, hidden visibility, zero opacity, or an inert
80
+ ancestor does not qualify merely because React kept that branch mounted. This
81
+ keeps right-panel state controls available without turning selection into a
82
+ layer import or live boot, while hiding axes that the captured frame cannot
83
+ actually reach. Legacy snapshots without a capture-time index are measured
84
+ after their inert DOM mounts.
82
85
 
83
86
  ## Automatic motion coverage
84
87
 
@@ -1056,6 +1056,56 @@ export function serializeStoryboardPreviewDocument(
1056
1056
  const clone = document.documentElement.cloneNode(true);
1057
1057
  if (!(clone instanceof HTMLElement)) return null;
1058
1058
 
1059
+ // A frozen snapshot preserves branches that are mounted but visually absent
1060
+ // (for example, two workspace panels cross-faded with opacity). Presence
1061
+ // alone therefore cannot tell the inspector which controls this captured
1062
+ // frame can actually reach. Record a compact, capture-time visibility index
1063
+ // on the body; older snapshots without it are measured again after mount.
1064
+ try {
1065
+ const reachableTestIds = new Set();
1066
+ const hasRenderedBox = (element) => {
1067
+ const rect = element.getBoundingClientRect();
1068
+ if (rect.width > 0 && rect.height > 0) return true;
1069
+ return [...element.querySelectorAll('*')].some((descendant) => {
1070
+ const descendantRect = descendant.getBoundingClientRect();
1071
+ return descendantRect.width > 0 && descendantRect.height > 0;
1072
+ });
1073
+ };
1074
+ const isReachable = (element) => {
1075
+ for (
1076
+ let current = element;
1077
+ current;
1078
+ current = current.parentElement
1079
+ ) {
1080
+ const style = window.getComputedStyle(current);
1081
+ const opacity = Number.parseFloat(style.opacity || '1');
1082
+ if (
1083
+ current.hasAttribute('hidden') ||
1084
+ current.hasAttribute('inert') ||
1085
+ style.display === 'none' ||
1086
+ style.visibility === 'hidden' ||
1087
+ style.visibility === 'collapse' ||
1088
+ (Number.isFinite(opacity) && opacity <= 0)
1089
+ ) {
1090
+ return false;
1091
+ }
1092
+ }
1093
+ return hasRenderedBox(element);
1094
+ };
1095
+ for (const element of document.documentElement.querySelectorAll('*')) {
1096
+ const testId = element.getAttribute('data-testid');
1097
+ if (testId && isReachable(element)) reachableTestIds.add(testId);
1098
+ }
1099
+ const cloneBody = clone.querySelector('body');
1100
+ cloneBody?.setAttribute(
1101
+ 'data-pygmalion-reachable-testids',
1102
+ encodeURIComponent(JSON.stringify([...reachableTestIds].sort())),
1103
+ );
1104
+ } catch {
1105
+ // Reachability metadata is additive. A constrained capture realm may not
1106
+ // implement layout; the mounted-preview fallback handles that snapshot.
1107
+ }
1108
+
1059
1109
  const sourceInputs = [...document.querySelectorAll('input')];
1060
1110
  const cloneInputs = [...clone.querySelectorAll('input')];
1061
1111
  sourceInputs.forEach((source, index) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pygmalionjs/pygmalion",
3
- "version": "0.6.28",
3
+ "version": "0.6.30",
4
4
  "description": "Code-backed DOM design sandbox and visual QA editor",
5
5
  "license": "UNLICENSED",
6
6
  "publishConfig": {