@pygmalionjs/pygmalion 0.5.14 → 0.5.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/dist-lib/{FrozenRoutePreview-BR5CBAD3.js → FrozenRoutePreview-CZOpvN6z.js} +1545 -1515
  2. package/dist-lib/pygmalion.js +5034 -4955
  3. package/dist-lib/testing.js +1 -1
  4. package/dist-lib/types/App.d.ts +5 -0
  5. package/dist-lib/types/canvas/CameraLayer.d.ts +9 -0
  6. package/dist-lib/types/canvas/Canvas.d.ts +19 -0
  7. package/dist-lib/types/canvas/FrameLabelBanner.d.ts +7 -0
  8. package/dist-lib/types/canvas/FrameView.d.ts +9 -0
  9. package/dist-lib/types/canvas/FrozenRoutePreview.d.ts +12 -0
  10. package/dist-lib/types/canvas/InspectOverlay.d.ts +10 -0
  11. package/dist-lib/types/canvas/LightweightCanvas.d.ts +10 -0
  12. package/dist-lib/types/canvas/ObjectControls.d.ts +22 -0
  13. package/dist-lib/types/canvas/Preview.d.ts +3 -0
  14. package/dist-lib/types/canvas/SectionBoxes.d.ts +24 -0
  15. package/dist-lib/types/canvas/ShadowRoutePreview.d.ts +14 -0
  16. package/dist-lib/types/canvas/StoryboardConnections.d.ts +31 -0
  17. package/dist-lib/types/canvas/cameraPlacement.d.ts +45 -0
  18. package/dist-lib/types/canvas/domTags.d.ts +10 -0
  19. package/dist-lib/types/canvas/layoutContext.d.ts +9 -0
  20. package/dist-lib/types/canvas/useFlowSession.d.ts +36 -0
  21. package/dist-lib/types/canvas/useFrameLod.d.ts +35 -0
  22. package/dist-lib/types/editor/adaptiveFrameLayout.d.ts +67 -0
  23. package/dist-lib/types/editor/assetEditing.d.ts +10 -0
  24. package/dist-lib/types/editor/bannerStyle.d.ts +18 -0
  25. package/dist-lib/types/editor/catalog.d.ts +101 -0
  26. package/dist-lib/types/editor/codegen.d.ts +11 -0
  27. package/dist-lib/types/editor/componentConnections.d.ts +63 -0
  28. package/dist-lib/types/editor/componentInstances.d.ts +27 -0
  29. package/dist-lib/types/editor/contentBounds.d.ts +34 -0
  30. package/dist-lib/types/editor/designCompiler.d.ts +100 -0
  31. package/dist-lib/types/editor/designImport.d.ts +448 -0
  32. package/dist-lib/types/editor/documentSync.d.ts +28 -0
  33. package/dist-lib/types/editor/domImport.d.ts +58 -0
  34. package/dist-lib/types/editor/editModePolicy.d.ts +17 -0
  35. package/dist-lib/types/editor/fiberMap.d.ts +16 -0
  36. package/dist-lib/types/editor/flowSessionScheduler.d.ts +97 -0
  37. package/dist-lib/types/editor/flowSessions.d.ts +137 -0
  38. package/dist-lib/types/editor/frameHeight.d.ts +14 -0
  39. package/dist-lib/types/editor/frameIdentity.d.ts +9 -0
  40. package/dist-lib/types/editor/frameLabels.d.ts +124 -0
  41. package/dist-lib/types/editor/frameLanes.d.ts +62 -0
  42. package/dist-lib/types/editor/frameLod.d.ts +123 -0
  43. package/dist-lib/types/editor/frameLodRuntime.d.ts +64 -0
  44. package/dist-lib/types/editor/framePointerHit.d.ts +67 -0
  45. package/dist-lib/types/editor/framePointerIntent.d.ts +53 -0
  46. package/dist-lib/types/editor/framePreviewChannels.d.ts +26 -0
  47. package/dist-lib/types/editor/framePreviewKeys.d.ts +21 -0
  48. package/dist-lib/types/editor/frameWheelRouting.d.ts +16 -0
  49. package/dist-lib/types/editor/frozenImport.d.ts +70 -0
  50. package/dist-lib/types/editor/host.d.ts +133 -0
  51. package/dist-lib/types/editor/inspect.d.ts +225 -0
  52. package/dist-lib/types/editor/interactiveStates.d.ts +103 -0
  53. package/dist-lib/types/editor/livePreviewInstances.d.ts +93 -0
  54. package/dist-lib/types/editor/liveScreens.d.ts +20 -0
  55. package/dist-lib/types/editor/nodeSelector.d.ts +20 -0
  56. package/dist-lib/types/editor/previewBootstrap.d.ts +189 -0
  57. package/dist-lib/types/editor/previewEnvironmentControls.d.ts +33 -0
  58. package/dist-lib/types/editor/previewHydration.d.ts +5 -0
  59. package/dist-lib/types/editor/previewIntent.d.ts +17 -0
  60. package/dist-lib/types/editor/previewSweep.d.ts +129 -0
  61. package/dist-lib/types/editor/previewWarmup.d.ts +77 -0
  62. package/dist-lib/types/editor/projectBootGate.d.ts +45 -0
  63. package/dist-lib/types/editor/projectRuntime.d.ts +138 -0
  64. package/dist-lib/types/editor/registry.d.ts +38 -0
  65. package/dist-lib/types/editor/routePreview.d.ts +285 -0
  66. package/dist-lib/types/editor/routePreviewArtifactV2.d.ts +34 -0
  67. package/dist-lib/types/editor/routePreviewArtifactV3.d.ts +64 -0
  68. package/dist-lib/types/editor/routePreviewPlacement.d.ts +9 -0
  69. package/dist-lib/types/editor/routePreviewStatus.d.ts +48 -0
  70. package/dist-lib/types/editor/scenarioCoverage.d.ts +167 -0
  71. package/dist-lib/types/editor/screenCards.d.ts +101 -0
  72. package/dist-lib/types/editor/screenDimensions.d.ts +63 -0
  73. package/dist-lib/types/editor/screenFlows.d.ts +47 -0
  74. package/dist-lib/types/editor/screenInteractions.d.ts +4 -0
  75. package/dist-lib/types/editor/screenLists.d.ts +40 -0
  76. package/dist-lib/types/editor/screenStateGroups.d.ts +36 -0
  77. package/dist-lib/types/editor/screenshotBitmaps.d.ts +20 -0
  78. package/dist-lib/types/editor/sectionHeaders.d.ts +117 -0
  79. package/dist-lib/types/editor/shadowPreview.d.ts +103 -0
  80. package/dist-lib/types/editor/sharedSource.d.ts +40 -0
  81. package/dist-lib/types/editor/sourceJournal.d.ts +27 -0
  82. package/dist-lib/types/editor/store.d.ts +838 -0
  83. package/dist-lib/types/editor/storyboardComposition.d.ts +116 -0
  84. package/dist-lib/types/editor/storyboardDiscovery.d.ts +105 -0
  85. package/dist-lib/types/editor/storyboardEnvironment.d.ts +22 -0
  86. package/dist-lib/types/editor/storyboardGraph.d.ts +120 -0
  87. package/dist-lib/types/editor/storyboardGraphRuntime.d.ts +12 -0
  88. package/dist-lib/types/editor/storyboardGraphView.d.ts +104 -0
  89. package/dist-lib/types/editor/surfaceDemands.d.ts +138 -0
  90. package/dist-lib/types/editor/tokens.d.ts +26 -0
  91. package/dist-lib/types/editor/useObserved.d.ts +1 -0
  92. package/dist-lib/types/editor/viewportPresets.d.ts +21 -0
  93. package/dist-lib/types/editor/visualQa.d.ts +50 -0
  94. package/dist-lib/types/lib.d.ts +365 -0
  95. package/dist-lib/types/shell/ApplyPanel.d.ts +1 -0
  96. package/dist-lib/types/shell/AssetsPanel.d.ts +3 -0
  97. package/dist-lib/types/shell/CanvasStatusProbe.d.ts +5 -0
  98. package/dist-lib/types/shell/CodePanel.d.ts +1 -0
  99. package/dist-lib/types/shell/ComponentStateControls.d.ts +108 -0
  100. package/dist-lib/types/shell/ContextMenu.d.ts +1 -0
  101. package/dist-lib/types/shell/LayerTree.d.ts +7 -0
  102. package/dist-lib/types/shell/PropsPanel.d.ts +1 -0
  103. package/dist-lib/types/shell/StoryboardGraphPanel.d.ts +23 -0
  104. package/dist-lib/types/shell/ZoomIndicator.d.ts +4 -0
  105. package/dist-lib/types/shell/qaPresentation.d.ts +9 -0
  106. package/dist-lib/types/testing.d.ts +8 -0
  107. package/dist-lib/types/ui/SourceRefControl.d.ts +38 -0
  108. package/dist-lib/types/ui/icons.d.ts +30 -0
  109. package/node/dev-mirror.mjs +63 -17
  110. package/package.json +7 -8
  111. package/testing.d.ts +0 -26
  112. package/types.d.ts +0 -2675
@@ -0,0 +1,103 @@
1
+ import type { DesignScreenInteraction, StoryboardEnvironment } from './designImport';
2
+ import type { PageModel } from './store';
3
+ /**
4
+ * Interactive states — axes a screen can be put into by acting on it.
5
+ *
6
+ * A collapsed sidebar or a hovered row is not a separate screen: it is the
7
+ * same screen after one gesture, reachable on every frame that has the
8
+ * control. Declaring it as an axis keeps those variations out of the
9
+ * catalog (where each would cost a captured frame) and puts them in the
10
+ * right panel, where a designer flips between them.
11
+ *
12
+ * The steps are ordinary interactions, so a selected option simply extends
13
+ * the frame's recipe. Everything downstream — the preview cache key, the
14
+ * live replay, the session walk — already keys off that recipe, which is
15
+ * why no supply path needs to know this feature exists.
16
+ */
17
+ export interface InteractiveStateOption {
18
+ id: string;
19
+ label: string;
20
+ /** Absent or empty marks the base state — the screen as captured. */
21
+ steps?: readonly DesignScreenInteraction[];
22
+ /**
23
+ * Boot condition this option puts the frame under, for states no gesture can
24
+ * reach: a failed request, an empty result, a stalled stream.
25
+ *
26
+ * Those are the states QA most wants to hold still, and until now they had no
27
+ * declaration. A gesture cannot produce them, so the only options were a
28
+ * dedicated capture (a frame per failure) or burying a mock flag inside one
29
+ * case's recipe — so hosts did neither, and their error/empty/loading markers
30
+ * sat in the product with nothing pointing at them.
31
+ *
32
+ * The environment is merged into the frame's own, which already belongs to the
33
+ * recipe identity — so selecting the option changes the cache key and the
34
+ * existing supply path (live replay, flow session, artifact lookup) reproduces
35
+ * it. Nothing downstream needs to know this option kind exists.
36
+ *
37
+ * May be combined with `steps` when the state needs a gesture AFTER booting
38
+ * under the condition.
39
+ */
40
+ environment?: StoryboardEnvironment;
41
+ /**
42
+ * Recognizes this option in the CAPTURED tree, which is what makes the base
43
+ * a property of the frame instead of the axis.
44
+ *
45
+ * One axis covers every frame carrying the control, but "the screen as
46
+ * captured" differs between them: a result panel captured mid-recording sits
47
+ * on its transcript tab, and the same panel captured after conversion sits on
48
+ * its summary tab. With a single declared base the axis is a no-op on every
49
+ * frame whose capture is the other option — selecting it only reproduces the
50
+ * capture that is already showing.
51
+ *
52
+ * Declare it on EVERY option (each with its own steps) and the axis resolves
53
+ * per frame: whichever option the capture already shows needs no steps, and
54
+ * the others run theirs.
55
+ */
56
+ capturedWhen?: {
57
+ testId: string;
58
+ attribute?: {
59
+ name: string;
60
+ value: string;
61
+ };
62
+ };
63
+ }
64
+ export interface InteractiveStateDef {
65
+ id: string;
66
+ label: string;
67
+ /**
68
+ * Frames the axis applies to. A frame qualifies when its route matches
69
+ * (prefix) and its imported tree carries the test id, so an axis declared
70
+ * once covers every screen that actually has the control.
71
+ */
72
+ requires?: {
73
+ testId?: string;
74
+ route?: string;
75
+ };
76
+ options: readonly InteractiveStateOption[];
77
+ }
78
+ export interface InteractiveStateDiagnostic {
79
+ severity: 'error' | 'warning';
80
+ interactionId: string;
81
+ message: string;
82
+ }
83
+ export declare function validateInteractiveStates(defs: readonly InteractiveStateDef[]): InteractiveStateDiagnostic[];
84
+ /**
85
+ * Axes available on one frame. The test-id requirement is checked against
86
+ * the imported tree, so an axis stays hidden until the frame actually shows
87
+ * the control it drives — a frame without a sidebar never offers to fold it.
88
+ */
89
+ export declare function interactiveStatesForPage(page: PageModel, defs: readonly InteractiveStateDef[]): InteractiveStateDef[];
90
+ /**
91
+ * The option this frame's capture already shows, or undefined when the axis
92
+ * declares a single base instead of witnessing each option.
93
+ */
94
+ export declare function capturedInteractiveOption(def: InteractiveStateDef, page: PageModel): InteractiveStateOption | undefined;
95
+ /**
96
+ * The option that reproduces the capture — the frame's base.
97
+ *
98
+ * A witnessed axis resolves it against this frame's tree; otherwise it is the
99
+ * one option declared without steps.
100
+ */
101
+ export declare function baseInteractiveOption(def: InteractiveStateDef, page?: PageModel): InteractiveStateOption | undefined;
102
+ export declare function setInteractiveStates(defs: readonly InteractiveStateDef[] | undefined): void;
103
+ export declare function getInteractiveStates(): readonly InteractiveStateDef[];
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Shared live application instances for producing screen previews.
3
+ *
4
+ * A frame that has no frozen snapshot yet has to run the real application once.
5
+ * Doing that per frame means one boot per screen, which is what made opening a
6
+ * catalog feel like waiting for a build. A screen whose state is declared does
7
+ * not need its own boot: the instance is already running, so the state is
8
+ * switched and the next snapshot is taken from the same document.
9
+ *
10
+ * A lease is exclusive. Snapshot production reads the document while it freezes,
11
+ * so two frames must never drive one instance at the same time.
12
+ */
13
+ export interface LivePreviewInstanceOptions {
14
+ /** Creates the offscreen frame. Injected so the pool can be tested without a browser. */
15
+ createIframe?: () => HTMLIFrameElement;
16
+ /** Where offscreen instances live. Defaults to the document body. */
17
+ resolveContainer?: () => HTMLElement | null;
18
+ /** Upper bound on booted instances. Groups beyond it fall back to their own frame. */
19
+ maximumInstances?: number;
20
+ }
21
+ export interface LivePreviewLease {
22
+ iframe: HTMLIFrameElement;
23
+ /** False on the boot that created the instance, true when an existing one was reused. */
24
+ reused: boolean;
25
+ release(): void;
26
+ }
27
+ export declare const LIVE_PREVIEW_INSTANCE_LIMIT = 4;
28
+ /**
29
+ * Names the group of screens that one running instance can serve.
30
+ *
31
+ * Screens agree on a group only when the application would boot identically:
32
+ * same origin, same route, same pre-mount environment, same viewport. Anything
33
+ * else is a different application state before the first render, and switching
34
+ * state cannot reach it.
35
+ */
36
+ export declare function livePreviewInstanceKey(input: {
37
+ origin: string;
38
+ route: string;
39
+ environment?: Record<string, unknown> | null;
40
+ width: number;
41
+ height: number;
42
+ }): string;
43
+ /**
44
+ * Reports whether a screen may be previewed on a shared running instance.
45
+ *
46
+ * Three things disqualify a screen. It has no declared group, so the host never
47
+ * promised the declaration is the whole state. It replays interactions, and only
48
+ * the screen knows how to undo what a click opened. Or it is the frame the
49
+ * designer is looking at, which is shown rather than only read, and imports its
50
+ * layers from the running document.
51
+ */
52
+ export declare function canShareLivePreviewInstance(page: {
53
+ session?: string;
54
+ route?: string | null;
55
+ interactions?: readonly unknown[];
56
+ layersImported?: boolean;
57
+ }, options?: {
58
+ detailActive?: boolean;
59
+ }): boolean;
60
+ export declare function createLivePreviewInstances(options?: LivePreviewInstanceOptions): {
61
+ lease: (input: {
62
+ key: string;
63
+ url: string;
64
+ width: number;
65
+ height: number;
66
+ }) => Promise<LivePreviewLease | null>;
67
+ /**
68
+ * Boots an instance for a group without keeping the lease.
69
+ *
70
+ * Warming pays the boot before any screen asks for it, so the first real
71
+ * lease of the group reuses a running document instead of waiting on a
72
+ * load. A warmed instance is an ordinary pool member afterwards: leases,
73
+ * discards, and failure handling treat it exactly like one that a frame
74
+ * booted itself.
75
+ *
76
+ * Returns true only when this call booted a new instance. Warming is
77
+ * best-effort and never throws — a failed boot is discarded through the
78
+ * lease path and reported as false.
79
+ */
80
+ warm(input: {
81
+ key: string;
82
+ url: string;
83
+ width: number;
84
+ height: number;
85
+ }): Promise<boolean>;
86
+ /** Drops an instance whose document can no longer be trusted. */
87
+ discard(key: string): void;
88
+ size(): number;
89
+ dispose(): void;
90
+ };
91
+ export type LivePreviewInstances = ReturnType<typeof createLivePreviewInstances>;
92
+ export declare function getLivePreviewInstances(): LivePreviewInstances;
93
+ export declare function resetLivePreviewInstances(): void;
@@ -0,0 +1,20 @@
1
+ export declare function subscribeLiveScreens(listener: () => void): () => void;
2
+ /**
3
+ * Sets how many frames may render live at once. Lowering it releases the
4
+ * least recently requested frames back to the frozen surface.
5
+ */
6
+ export declare function setLiveScreenBudget(next: number): void;
7
+ export declare function getLiveScreenBudget(): number;
8
+ /**
9
+ * Claims a live slot for a page, or refreshes its recency when it already
10
+ * holds one. Returns whether the page holds a slot afterwards, so a caller
11
+ * can fall back to the frozen surface without waiting for a re-render.
12
+ */
13
+ export declare function requestLiveScreen(pageId: string): boolean;
14
+ /** Releases a page's slot, if it holds one. */
15
+ export declare function releaseLiveScreen(pageId: string): void;
16
+ export declare function holdsLiveScreen(pageId: string): boolean;
17
+ /** Page ids currently rendering live, least recently requested first. */
18
+ export declare function liveScreenHolders(): readonly string[];
19
+ /** Test seam — drops every claim and the budget. */
20
+ export declare function resetLiveScreens(): void;
@@ -0,0 +1,20 @@
1
+ import type { NodeModel } from './store';
2
+ export interface NodeSelector {
3
+ testId?: string;
4
+ classIncludes?: string | readonly string[];
5
+ }
6
+ export declare function nodeMatchesSelector(node: NodeModel, selector: NodeSelector): boolean;
7
+ /**
8
+ * Matching nodes in document order, the subtree root included. The cap keeps
9
+ * a careless declaration (a class prefix that matches half the screen) from
10
+ * filling the panel with hundreds of controls.
11
+ */
12
+ export declare function collectMatchingNodes(root: NodeModel, matches: (node: NodeModel) => boolean, limit: number): NodeModel[];
13
+ /** Chain from the subtree root down to the node, or null when unrelated. */
14
+ export declare function pathToNode(root: NodeModel, id: string): NodeModel[] | null;
15
+ /**
16
+ * The first few words a subtree shows, used to name a row in the panel.
17
+ * Captured markup carries its identity in text (a chart number, a name, an
18
+ * age), so the leading text is what a designer recognizes a row by.
19
+ */
20
+ export declare function leadingText(node: NodeModel, maxLength?: number): string;
@@ -0,0 +1,189 @@
1
+ import type { RoutePreviewArtifactBundle } from './routePreview.js';
2
+ import type { DesignScreenCase, StoryboardEnvironment } from './designImport';
3
+ export interface PreviewRecipeIdentity {
4
+ id: string;
5
+ recipe: unknown;
6
+ }
7
+ export interface PreviewCacheNamespaceOptions {
8
+ sourceRevision: string;
9
+ recipes: readonly PreviewRecipeIdentity[];
10
+ recipeVersion?: number;
11
+ scope?: string;
12
+ }
13
+ export type StoryboardCaptureRecipeInput = Pick<DesignScreenCase, 'route' | 'path' | 'componentName' | 'gallery' | 'width' | 'height' | 'environment' | 'preset' | 'interactions' | 'assertions'>;
14
+ export interface PreviewArtifactExpectation {
15
+ namespace: string;
16
+ sourceRevision: string;
17
+ }
18
+ export interface PreviewArtifactFrameRequest {
19
+ id: string;
20
+ fingerprint?: string;
21
+ }
22
+ export interface PreviewArtifactTransportRequest extends PreviewArtifactExpectation {
23
+ /** Frames to fetch. Omitted asks for the whole bundle, as earlier versions did. */
24
+ frames?: readonly PreviewArtifactFrameRequest[];
25
+ /** Set false to seed only what exists instead of capturing what is absent. */
26
+ generate?: boolean;
27
+ /** Return only exact/missing/stale classification, without frame payloads. */
28
+ resolveOnly?: boolean;
29
+ signal?: AbortSignal;
30
+ captureBaseUrl?: string;
31
+ }
32
+ export type PreviewArtifactTransport = (request: PreviewArtifactTransportRequest) => Promise<unknown | null | undefined>;
33
+ export interface PreviewArtifactHttpResponse {
34
+ readonly ok: boolean;
35
+ readonly status?: number;
36
+ readonly statusText?: string;
37
+ json(): Promise<unknown>;
38
+ }
39
+ export type PreviewArtifactFetch = (input: string, init: {
40
+ method: 'GET';
41
+ headers: {
42
+ accept: 'application/json';
43
+ };
44
+ cache: 'no-store';
45
+ signal?: AbortSignal;
46
+ }) => Promise<PreviewArtifactHttpResponse>;
47
+ export interface PreviewArtifactHttpTransportOptions {
48
+ endpoint: string;
49
+ fetcher?: PreviewArtifactFetch;
50
+ captureBaseUrl?: string;
51
+ }
52
+ export type PreviewArtifactValidationReason = 'invalid-bundle' | 'namespace-mismatch' | 'revision-mismatch';
53
+ /** Exactness of the frames named by a partial artifact request. */
54
+ export interface PreviewArtifactFrameResolution {
55
+ /** Frames whose source revision or opaque fingerprint matches the request. */
56
+ exact: readonly string[];
57
+ /** Frames that were not present in the returned artifact. */
58
+ missing: readonly string[];
59
+ /** Frames that were present but did not match the requested identity. */
60
+ stale: readonly string[];
61
+ }
62
+ export declare const PREVIEW_ARTIFACT_FRAME_REQUEST_CHUNK_SIZE = 20;
63
+ /** Keeps GET-based local transports below common query/header limits. */
64
+ export declare function chunkPreviewArtifactFrameRequests(frames: readonly PreviewArtifactFrameRequest[], chunkSize?: number): PreviewArtifactFrameRequest[][];
65
+ export type PreviewArtifactValidationResult = {
66
+ valid: true;
67
+ artifact: RoutePreviewArtifactBundle;
68
+ errors: readonly [];
69
+ } | {
70
+ valid: false;
71
+ artifact: null;
72
+ reason: PreviewArtifactValidationReason;
73
+ errors: readonly string[];
74
+ };
75
+ export type PreviewArtifactBootstrapResult = {
76
+ status: 'accepted';
77
+ artifact: RoutePreviewArtifactBundle;
78
+ /** Present when the caller asked for individual frames. */
79
+ frameResolution?: PreviewArtifactFrameResolution;
80
+ } | {
81
+ status: 'unavailable';
82
+ artifact: null;
83
+ error?: unknown;
84
+ } | {
85
+ status: 'rejected';
86
+ artifact: null;
87
+ reason: PreviewArtifactValidationReason;
88
+ errors: readonly string[];
89
+ };
90
+ export type PreviewArtifactFrameProbeResult = {
91
+ status: 'accepted';
92
+ resolution: PreviewArtifactFrameResolution;
93
+ } | {
94
+ status: 'unavailable';
95
+ resolution: null;
96
+ error?: unknown;
97
+ } | {
98
+ status: 'rejected';
99
+ resolution: null;
100
+ errors: readonly string[];
101
+ };
102
+ export declare function createPreviewRecipeFingerprint(recipes: readonly PreviewRecipeIdentity[]): string;
103
+ /**
104
+ * Identity of one frame's capture, so freshness is decided per screen.
105
+ *
106
+ * The namespace fingerprints the whole catalog, which is why one edited screen
107
+ * used to invalidate every frame. This value covers a single screen's recipe and,
108
+ * when a caller passes one, the revision or content digest that screen depends
109
+ * on — the piece a dependency-closure digest replaces later.
110
+ */
111
+ export declare function createPreviewFrameFingerprint(screen: StoryboardCaptureRecipeInput & {
112
+ id?: string;
113
+ }, options?: {
114
+ dependencyDigest?: string;
115
+ }): string;
116
+ /**
117
+ * Per-route content digests published by the storyboard scan.
118
+ *
119
+ * A source revision remains the provenance boundary. Within that boundary, the
120
+ * route digest makes an edited screen move without invalidating unrelated ones.
121
+ */
122
+ export declare function setPreviewDependencyDigests(routes: readonly {
123
+ path?: string;
124
+ dependencyDigest?: string;
125
+ }[] | null): void;
126
+ export declare function previewDependencyDigest(route: string): string | undefined;
127
+ export interface PreviewFramePage {
128
+ canonicalId: string;
129
+ route: string;
130
+ width?: number;
131
+ height?: number;
132
+ environment?: StoryboardEnvironment;
133
+ preset?: unknown;
134
+ interactions?: unknown;
135
+ assertions?: unknown;
136
+ }
137
+ /**
138
+ * Identity of one frame's capture, derived the same way wherever it is needed.
139
+ *
140
+ * The revision is folded in until a dependency-closure digest replaces it, so a
141
+ * frame is invalidated no less often than before; what changes is that a consumer
142
+ * now captures per frame instead of demanding the whole catalog up front.
143
+ */
144
+ export declare function createRoutePreviewFrameFingerprint(page: PreviewFramePage, previewRevision: string, baselineEnvironment?: StoryboardEnvironment): string;
145
+ export declare function createPreviewCacheNamespace({ sourceRevision, recipes, recipeVersion, scope, }: PreviewCacheNamespaceOptions): string;
146
+ /**
147
+ * Device pixels captured per CSS pixel. Must mirror
148
+ * `STORYBOARD_CAPTURE_SCREENSHOT_SCALE` in `storyboard-capture-runtime.mjs`
149
+ * (browser code cannot import the Node runtime); a shared test pins the pair.
150
+ */
151
+ export declare const STORYBOARD_CAPTURE_RECIPE_SCREENSHOT_SCALE = 2;
152
+ /**
153
+ * Returns the portable runtime identity of a resolved storyboard capture.
154
+ * Descriptive labels and editor-only grouping are intentionally omitted.
155
+ * The screenshot scale participates so that changing the capture density
156
+ * retires every artifact captured at the old density.
157
+ */
158
+ export declare function createStoryboardCaptureRecipeIdentity(screen: StoryboardCaptureRecipeInput): unknown;
159
+ /**
160
+ * Creates the default same-origin HTTP transport for preview artifacts.
161
+ * Hosts provide only an endpoint; identity query parameters and safe fetch
162
+ * semantics remain owned by Pygmalion.
163
+ */
164
+ export declare function createPreviewArtifactHttpTransport({ endpoint, fetcher, captureBaseUrl, }: PreviewArtifactHttpTransportOptions): PreviewArtifactTransport;
165
+ /** Resolves frame freshness without downloading or validating snapshot payloads. */
166
+ export declare function probePreviewArtifactFrames({ namespace, sourceRevision, transport, signal, frames, }: PreviewArtifactExpectation & {
167
+ transport: PreviewArtifactTransport;
168
+ signal?: AbortSignal;
169
+ frames: readonly PreviewArtifactFrameRequest[];
170
+ }): Promise<PreviewArtifactFrameProbeResult>;
171
+ export declare function validatePreviewArtifactBundle(value: unknown, expectation?: PreviewArtifactExpectation, options?: {
172
+ perFrameIdentity?: boolean;
173
+ }): PreviewArtifactValidationResult;
174
+ /**
175
+ * Resolves partial artifact exactness per requested frame.
176
+ *
177
+ * A bundle revision is sufficient for v1/v2. Version 3 requires both the source
178
+ * revision and opaque fingerprint; matching content from another revision stays
179
+ * useful as last-known evidence but never becomes exact.
180
+ * This keeps a valid partial response distinct from a current frame response:
181
+ * an empty or stale subset is valid transport data, but it is not a warm cache.
182
+ */
183
+ export declare function resolvePreviewArtifactFrames(artifact: RoutePreviewArtifactBundle, expectation: PreviewArtifactExpectation, frames: readonly PreviewArtifactFrameRequest[]): PreviewArtifactFrameResolution;
184
+ export declare function bootstrapPreviewArtifact({ namespace, sourceRevision, transport, signal, frames, generate, }: PreviewArtifactExpectation & {
185
+ transport: PreviewArtifactTransport;
186
+ signal?: AbortSignal;
187
+ frames?: readonly PreviewArtifactFrameRequest[];
188
+ generate?: boolean;
189
+ }): Promise<PreviewArtifactBootstrapResult>;
@@ -0,0 +1,33 @@
1
+ import type { StoryboardEnvironment } from './designImport';
2
+ /** A host-declared value for a portable browser condition such as color scheme. */
3
+ export interface PreviewEnvironmentControlOptionDef {
4
+ value: string;
5
+ label: string;
6
+ description?: string;
7
+ environment?: StoryboardEnvironment;
8
+ }
9
+ /**
10
+ * An orthogonal preview condition. These controls do not create storyboard
11
+ * nodes: they temporarily alter only the active frame's live route preview.
12
+ */
13
+ export interface PreviewEnvironmentControlDef {
14
+ id: string;
15
+ label: string;
16
+ description?: string;
17
+ defaultValue: string;
18
+ options: readonly PreviewEnvironmentControlOptionDef[];
19
+ }
20
+ /** Configure the inspector controls once from the host application. */
21
+ export declare function setPreviewEnvironmentControls(next: readonly PreviewEnvironmentControlDef[]): void;
22
+ export declare function getPreviewEnvironmentControls(): readonly PreviewEnvironmentControlDef[];
23
+ export declare function getPreviewEnvironmentControlValue(controlId: string): string | undefined;
24
+ /**
25
+ * Selects a condition. Defaults are visually selected but remain inert until a
26
+ * user changes a control, preserving existing exact preview artifact identities.
27
+ */
28
+ export declare function setPreviewEnvironmentControlValue(controlId: string, value: string): void;
29
+ export declare function clearPreviewEnvironmentControlValues(): void;
30
+ /** Merged explicit override for the active frame, if the user selected one. */
31
+ export declare function getPreviewEnvironmentOverride(): StoryboardEnvironment | undefined;
32
+ export declare function subscribePreviewEnvironmentControls(listener: () => void): () => void;
33
+ export declare function getPreviewEnvironmentControlRevision(): number;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Produces a content identity for a set of preview cache keys. Runtime priority
3
+ * may reorder the keys, but that must not invalidate an already hydrated set.
4
+ */
5
+ export declare function createPreviewHydrationSignature(keys: readonly string[]): string;
@@ -0,0 +1,17 @@
1
+ export declare const PREVIEW_INTENT_LIMIT = 2;
2
+ /** Marks a frame as likely to be opened next. Returns true when the slot set changed. */
3
+ export declare function notePreviewIntent(pageId: string, limit?: number): boolean;
4
+ /**
5
+ * Drops preparation for a frame.
6
+ *
7
+ * Leaving a frame is not a reason to drop it — the work is already paid for and
8
+ * the designer often comes back. This exists for frames that stop being
9
+ * previewable at all, such as one whose layers have been imported.
10
+ */
11
+ export declare function releasePreviewIntent(pageId: string): void;
12
+ export declare function hasPreviewIntent(pageId: string): boolean;
13
+ export declare function getPreviewIntentSlots(): readonly string[];
14
+ export declare function subscribePreviewIntent(listener: () => void): () => void;
15
+ /** Fires only when this page enters or leaves the intent slots. */
16
+ export declare function subscribePreviewIntentFor(pageId: string, listener: () => void): () => void;
17
+ export declare function resetPreviewIntent(): void;
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Background capture sweep planning and driving.
3
+ *
4
+ * A cold canvas shows placeholders wherever no frozen snapshot exists yet, and
5
+ * the editor only produces snapshots for frames the designer touches. The sweep
6
+ * fills the rest while the editor is idle: it plans the missing screens once,
7
+ * then produces them one at a time at a priority below all user work, joining
8
+ * any production a user action already started for the same screen.
9
+ *
10
+ * The planner is pure so ordering is testable; the driver takes injected
11
+ * dependencies so it can run in tests without a browser.
12
+ */
13
+ export interface SweepFrameIdentity {
14
+ id: string;
15
+ fingerprint?: string;
16
+ }
17
+ export interface SweepCandidate {
18
+ pageId: string;
19
+ /** Persistent recipe cache key of the screen. */
20
+ key: string;
21
+ /**
22
+ * Producer dedup key shared with the frame's own producer, so a user opening
23
+ * a screen mid-sweep joins the in-flight production instead of restarting it.
24
+ */
25
+ runtimeKey: string;
26
+ /**
27
+ * Shared live instance group when the screen may run on the boot pool,
28
+ * null when the screen needs its own throwaway frame.
29
+ */
30
+ groupKey: string | null;
31
+ /** Interaction screens are never booted by the sweep; they are delegated. */
32
+ hasInteractions: boolean;
33
+ /**
34
+ * Screen-space distance from the viewport center. The caller encodes
35
+ * "active canvas first" by giving frames on other canvases an infinite
36
+ * distance, so nearby visible work is always produced before offscreen work.
37
+ */
38
+ distance: number;
39
+ /** Exact-artifact identity used when the screen is delegated to capture. */
40
+ frame?: SweepFrameIdentity;
41
+ }
42
+ export interface SweepUnit {
43
+ pageId: string;
44
+ key: string;
45
+ runtimeKey: string;
46
+ groupKey: string | null;
47
+ /** Planned position. The producer priority derives from it so earlier units run first. */
48
+ index: number;
49
+ }
50
+ export interface PreviewSweepPlan {
51
+ units: SweepUnit[];
52
+ interactionFrames: SweepFrameIdentity[];
53
+ }
54
+ export interface PlanPreviewSweepOptions {
55
+ /**
56
+ * Screens that already have a snapshot are not swept. A warm reopen finds
57
+ * every key present after prehydration, so it plans zero units.
58
+ */
59
+ hasSnapshot?: (key: string) => boolean;
60
+ }
61
+ /** Screen-space distance from the viewport center to a frame center. */
62
+ export declare function sweepViewportDistance(frame: {
63
+ x: number;
64
+ y: number;
65
+ width: number;
66
+ height: number;
67
+ }, camera: {
68
+ panX: number;
69
+ panY: number;
70
+ zoom: number;
71
+ viewportWidth: number;
72
+ viewportHeight: number;
73
+ }): number;
74
+ /**
75
+ * Orders missing screens for background production.
76
+ *
77
+ * Distance ascending, with members of one instance group kept contiguous at
78
+ * the position of their nearest member — the editor-side mirror of "one boot,
79
+ * many screens": one lease serves the whole group by switching declared state
80
+ * between serializations. Interaction screens are split out for delegation.
81
+ */
82
+ export declare function planPreviewSweep(candidates: readonly SweepCandidate[], options?: PlanPreviewSweepOptions): PreviewSweepPlan;
83
+ export type PreviewSweepPhase = 'idle' | 'running' | 'paused' | 'done' | 'cancelled';
84
+ export interface PreviewSweepStatus {
85
+ phase: PreviewSweepPhase;
86
+ total: number;
87
+ completed: number;
88
+ failed: number;
89
+ }
90
+ export interface PreviewSweepLease {
91
+ /** True when the pool served an already booted instance. */
92
+ reused?: boolean;
93
+ /** The live document a unit serializes from. */
94
+ iframe?: HTMLIFrameElement;
95
+ release(): void;
96
+ /** Drops the instance behind the lease when its document can no longer be trusted. */
97
+ discard(): void;
98
+ }
99
+ export interface PreviewSweeperDeps {
100
+ /**
101
+ * Provides the live document for one unit: a pooled lease for grouped
102
+ * screens, an ephemeral offscreen frame otherwise.
103
+ */
104
+ leaseInstance(unit: SweepUnit): Promise<PreviewSweepLease | null>;
105
+ /** Produces and stores the snapshot for one unit off the leased document. */
106
+ produce(unit: SweepUnit, lease: PreviewSweepLease | null, isCancelled: () => boolean): Promise<unknown>;
107
+ /** Delegates interaction screens to the exact capture pipeline, one call per chunk. */
108
+ requestArtifactFrames(frames: readonly SweepFrameIdentity[]): unknown;
109
+ /** Checked before each unit; a paused sweep retries on a short backoff. */
110
+ shouldPause(): boolean;
111
+ now?: () => number;
112
+ log?: (message: string) => void;
113
+ }
114
+ export interface PreviewSweeperOptions {
115
+ pauseBackoffMs?: number;
116
+ interactionChunkSize?: number;
117
+ interactionRequestCap?: number;
118
+ }
119
+ export interface PreviewSweeper {
120
+ /** Runs one plan to completion. A later run or cancel abandons this one. */
121
+ run(plan: PreviewSweepPlan): Promise<void>;
122
+ /** Releases the held lease and invalidates in-flight unit completions. */
123
+ cancel(): void;
124
+ }
125
+ export declare const SWEEP_INTERACTION_CHUNK_SIZE = 8;
126
+ export declare const SWEEP_INTERACTION_REQUEST_CAP = 32;
127
+ export declare function getPreviewSweepStatus(): PreviewSweepStatus;
128
+ export declare function subscribePreviewSweep(listener: () => void): () => void;
129
+ export declare function createPreviewSweeper(deps: PreviewSweeperDeps, options?: PreviewSweeperOptions): PreviewSweeper;