@pygmalionjs/pygmalion 0.11.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/dist-lib/{CameraLayer-BZ8HgrUG.js → CameraLayer-CGW_FPQS.js} +9 -9
  2. package/dist-lib/pygmalion.js +12117 -11103
  3. package/dist-lib/{runtime-Bxn8116i.js → runtime-DkCf4iKe.js} +80 -78
  4. package/dist-lib/style.css +1 -1
  5. package/dist-lib/testing.js +15 -15
  6. package/dist-lib/types/document/prototype.d.ts +32 -2
  7. package/dist-lib/types/document/prototypeDrag.d.ts +43 -0
  8. package/dist-lib/types/document/prototypeEasing.d.ts +3 -0
  9. package/dist-lib/types/document/prototypeMotion.d.ts +25 -0
  10. package/dist-lib/types/document/prototypeMotionContracts.d.ts +30 -0
  11. package/dist-lib/types/document/prototypeSpring.d.ts +28 -0
  12. package/dist-lib/types/lib.d.ts +10 -0
  13. package/dist-lib/types/token-library/cache.d.ts +20 -0
  14. package/dist-lib/types/token-library/formatReferences.d.ts +12 -0
  15. package/dist-lib/types/workspace/WorkspaceConditions.d.ts +7 -0
  16. package/dist-lib/types/workspace/WorkspaceShell.d.ts +1 -1
  17. package/dist-lib/types/workspace/edit/projection.d.ts +2 -0
  18. package/dist-lib/types/workspace/view/StoryboardWorkspace.d.ts +4 -2
  19. package/dist-lib/types/workspace/view/prototypeInputFrame.d.ts +13 -0
  20. package/dist-lib/types/workspace/view/prototypeMotionDom.d.ts +5 -0
  21. package/dist-lib/types/workspace/view/prototypePlayback.d.ts +14 -0
  22. package/dist-lib/types/workspace/view/storyboardCanvas.d.ts +42 -0
  23. package/dist-lib/types/workspace/view/storyboardCaptures.d.ts +26 -0
  24. package/dist-lib/types/workspace/view/useStoryboardCaptures.d.ts +5 -0
  25. package/node/preview-capture-worker.mjs +17 -3
  26. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
- import { bW as l, T as u } from "./runtime-Bxn8116i.js";
2
- import { bi as y, cc as b, cd as E, ce as P, be as T, as as $ } from "./runtime-Bxn8116i.js";
3
- const v = {
1
+ import { bY as l, T as u } from "./runtime-DkCf4iKe.js";
2
+ import { bk as y, ce as b, cf as E, cg as P, bg as T, as as $ } from "./runtime-DkCf4iKe.js";
3
+ const f = {
4
4
  mirror: ["status", "refresh", "refs"],
5
5
  session: ["open", "check", "previewVisual", "applyVisual", "revertVisual"],
6
6
  inspect: ["impact"],
@@ -8,12 +8,12 @@ const v = {
8
8
  preview: ["artifact", "progress", "catalog"],
9
9
  storyboard: ["environment", "sourceUsage", "componentBranches"]
10
10
  };
11
- function g(m = {}, t = {}) {
12
- const n = [], d = t.origin ?? "", e = (r) => {
11
+ function v(m = {}, t = {}) {
12
+ const n = [], d = t.origin ?? "", r = (e) => {
13
13
  var a;
14
14
  const i = {};
15
- for (const o of v[r]) {
16
- const s = `${r}.${String(o)}`, c = (a = m[r]) == null ? void 0 : a[o];
15
+ for (const o of f[e]) {
16
+ const s = `${e}.${String(o)}`, c = (a = m[e]) == null ? void 0 : a[o];
17
17
  i[String(o)] = (...p) => (n.push({ operation: s, request: p[0] }), c ? Promise.resolve(c(...p)) : Promise.reject(
18
18
  new u({
19
19
  operation: s,
@@ -27,13 +27,13 @@ function g(m = {}, t = {}) {
27
27
  return {
28
28
  calls: n,
29
29
  endpoints: l(t.endpoints),
30
- resolveUrl: (r) => `${d}${r}`,
31
- mirror: e("mirror"),
32
- session: e("session"),
33
- inspect: e("inspect"),
34
- qa: e("qa"),
35
- preview: e("preview"),
36
- storyboard: e("storyboard")
30
+ resolveUrl: (e) => `${d}${e}`,
31
+ mirror: r("mirror"),
32
+ session: r("session"),
33
+ inspect: r("inspect"),
34
+ qa: r("qa"),
35
+ preview: r("preview"),
36
+ storyboard: r("storyboard")
37
37
  };
38
38
  }
39
39
  export {
@@ -42,6 +42,6 @@ export {
42
42
  E as FrozenRoutePreviewView,
43
43
  P as NodeModel,
44
44
  T as createEditorRuntime,
45
- g as createMemoryTransport,
45
+ v as createMemoryTransport,
46
46
  $ as serializeNode
47
47
  };
@@ -9,6 +9,8 @@
9
9
  */
10
10
  import type { DesignDocument, DocumentDiagnostic, DocumentDuplicateMetadataRemapper, DocumentNode, DocumentResolveOptions, DocumentTransaction, ResolvedDocumentNode } from './contracts.js';
11
11
  import type { PrototypeOccurrence, PrototypeRowPath } from './prototypeOccurrence.js';
12
+ import type { PrototypeDragAddress, PrototypeDragConfiguration } from './prototypeMotionContracts.js';
13
+ import { type PrototypeSpringPlan } from './prototypeSpring.js';
12
14
  export type { PrototypeOccurrence, PrototypeRowPath } from './prototypeOccurrence.js';
13
15
  export declare const PROTOTYPE_METADATA_KEY = "prototype";
14
16
  export declare const PROTOTYPE_INTERACTIONS_PROPERTY = "interactions";
@@ -21,6 +23,7 @@ export interface PrototypeTrigger {
21
23
  type: PrototypeTriggerType;
22
24
  key?: string;
23
25
  delayMs?: number;
26
+ drag?: PrototypeDragConfiguration;
24
27
  }
25
28
  export declare const prototypeTransitionTypes: readonly ["instant", "dissolve", "smart-animate", "move-in", "move-out", "push", "slide-in", "slide-out"];
26
29
  export type PrototypeTransitionType = typeof prototypeTransitionTypes[number];
@@ -244,6 +247,9 @@ export interface PrototypeSceneLayer {
244
247
  path: string[];
245
248
  /** Names from the scene root down to this layer, the smart-animate hierarchy key. */
246
249
  namePath: string;
250
+ /** Opaque name segments; slashes in a name never form a hierarchy. */
251
+ nameSegments: string[];
252
+ instancePath: readonly string[];
247
253
  name: string;
248
254
  kind: DocumentNode['kind'];
249
255
  x: number;
@@ -285,9 +291,17 @@ export interface PrototypeSmartAnimatePlan {
285
291
  pairs: PrototypeSmartAnimatePair[];
286
292
  leaving: PrototypeSceneLayer[];
287
293
  entering: PrototypeSceneLayer[];
294
+ diagnostics: {
295
+ code: string;
296
+ message: string;
297
+ }[];
288
298
  }
289
- /** Smart animate pairs equal names within equal hierarchy paths. Unmatched layers fade. */
290
- export declare function matchSmartAnimateLayers(document: DesignDocument, fromScreenId: string, toScreenId: string, variants?: Readonly<Record<string, string>>): PrototypeSmartAnimatePlan;
299
+ /** Model and DOM playback share occurrence-aware matching; ambiguous layers remain unmatched. */
300
+ export declare function matchSmartAnimateLayers(document: DesignDocument, fromScreenId: string, toScreenId: string, variants?: Readonly<Record<string, string>>, options?: {
301
+ from: PrototypeState;
302
+ to: PrototypeState;
303
+ context?: PrototypeRuntimeContext;
304
+ }): PrototypeSmartAnimatePlan;
291
305
  export interface PrototypeTimer {
292
306
  id: string;
293
307
  at: number;
@@ -337,6 +351,8 @@ export interface PrototypeTransitionState {
337
351
  transition: PrototypeTransition;
338
352
  startedAt: number;
339
353
  endsAt: number;
354
+ /** Physical spring plan shared by the interpreter deadline and the renderer. */
355
+ spring?: PrototypeSpringPlan;
340
356
  }
341
357
  export interface PrototypeState {
342
358
  flowId: string | null;
@@ -441,5 +457,19 @@ export interface PrototypeStartOptions {
441
457
  export declare function createPrototypeState(document: DesignDocument, options?: PrototypeStartOptions): PrototypeState;
442
458
  /** The earliest moment the state changes without input, or null when it is at rest. */
443
459
  export declare function prototypeNextDeadline(state: PrototypeState): number | null;
460
+ export type PrototypeDragPreparation = {
461
+ status: 'none';
462
+ } | {
463
+ status: 'fallback';
464
+ message: string;
465
+ } | {
466
+ status: 'ready';
467
+ configuration: PrototypeDragConfiguration;
468
+ state: PrototypeState;
469
+ effects: PrototypeEffect[];
470
+ transition: PrototypeTransition;
471
+ };
472
+ /** Prepare a captured target without advancing timers or publishing effects. Authoring stays untouched. */
473
+ export declare function preparePrototypeDrag(document: DesignDocument, previous: PrototypeState, address: PrototypeDragAddress, now: number, runtime?: PrototypeRuntimeContext): PrototypeDragPreparation;
444
474
  /** One pure step: the event (or the passage of time) against a frozen document; time is `now`. */
445
475
  export declare function stepPrototype(document: DesignDocument, previous: PrototypeState, event: PrototypeEvent, now: number, runtime?: PrototypeRuntimeContext): PrototypeStepResult;
@@ -0,0 +1,43 @@
1
+ import type { PrototypeDragAddress, PrototypeDragConfiguration, PrototypeDragPoint } from './prototypeMotionContracts.js';
2
+ export interface PrototypeDragSampleInput {
3
+ pointerId: number;
4
+ point: PrototypeDragPoint;
5
+ /** Injected logical time in milliseconds. */
6
+ now: number;
7
+ }
8
+ export interface PrototypeDragStartInput extends PrototypeDragSampleInput, PrototypeDragAddress {
9
+ }
10
+ export interface PrototypeDragFinishInput extends PrototypeDragSampleInput {
11
+ cancel?: boolean;
12
+ }
13
+ export interface PrototypeDragSession {
14
+ readonly config: Readonly<PrototypeDragConfiguration>;
15
+ readonly address: {
16
+ readonly path: readonly string[];
17
+ readonly occurrence?: {
18
+ readonly sceneId: string;
19
+ readonly rowPath: readonly Readonly<{
20
+ repeatId: string;
21
+ key: string | number;
22
+ }>[];
23
+ };
24
+ };
25
+ readonly pointerId: number;
26
+ readonly origin: Readonly<PrototypeDragPoint>;
27
+ readonly lastPoint: Readonly<PrototypeDragPoint>;
28
+ readonly startedAt: number;
29
+ readonly updatedAt: number;
30
+ readonly progress: number;
31
+ /** Velocity of the clamped progress in progress units per second. */
32
+ readonly velocity: number;
33
+ readonly phase: 'active' | 'ended';
34
+ readonly outcome: 'commit' | 'cancel' | null;
35
+ }
36
+ export declare function isPrototypeDragConfiguration(value: unknown): value is PrototypeDragConfiguration;
37
+ /** Capture one gesture without resolving scenes, dispatching actions, or reading a clock. */
38
+ export declare function startPrototypeDrag(config: PrototypeDragConfiguration, input: PrototypeDragStartInput): PrototypeDragSession;
39
+ /** Other pointers and samples after release are inert. Invalid owned samples leave the session unchanged. */
40
+ export declare function updatePrototypeDrag(session: PrototypeDragSession, input: PrototypeDragSampleInput): PrototypeDragSession;
41
+ /** End once. Only position determines commit. An unchanged release axis retains its last
42
+ * velocity for at most 80ms; a longer pause rests. Cancellation never commits. */
43
+ export declare function finishPrototypeDrag(session: PrototypeDragSession, input: PrototypeDragFinishInput): PrototypeDragSession;
@@ -0,0 +1,3 @@
1
+ import { type PrototypeEasing } from './prototype.js';
2
+ /** Sample an authored non-spring timing curve, preserving back-curve overshoot. */
3
+ export declare function samplePrototypeEasing(easing: PrototypeEasing, progress: number): number;
@@ -0,0 +1,25 @@
1
+ import type { PrototypeRowPath } from './prototypeOccurrence.js';
2
+ /** Names are opaque relative segments; the scene root name is excluded. */
3
+ export interface PrototypeMotionLayer<T> {
4
+ nodeId: string;
5
+ names: readonly string[];
6
+ kind: string;
7
+ rowPath: PrototypeRowPath;
8
+ instancePath: readonly string[];
9
+ root?: boolean;
10
+ value: T;
11
+ }
12
+ export interface PrototypeMotionMatch<T> {
13
+ pairs: {
14
+ from: PrototypeMotionLayer<T>;
15
+ to: PrototypeMotionLayer<T>;
16
+ }[];
17
+ leaving: PrototypeMotionLayer<T>[];
18
+ entering: PrototypeMotionLayer<T>[];
19
+ diagnostics: {
20
+ code: string;
21
+ message: string;
22
+ }[];
23
+ }
24
+ /** Uncertain matches remain separate so the caller can crossfade them. Caller values stay untouched. */
25
+ export declare function matchPrototypeMotionLayers<T>(from: readonly PrototypeMotionLayer<T>[], to: readonly PrototypeMotionLayer<T>[]): PrototypeMotionMatch<T>;
@@ -0,0 +1,30 @@
1
+ import type { PrototypeOccurrence } from './prototypeOccurrence.js';
2
+ /** A transient scalar sample. Velocity is measured in progress units per second. */
3
+ export interface PrototypeMotionSample {
4
+ progress: number;
5
+ velocity: number;
6
+ finished: boolean;
7
+ reason?: 'settled' | 'duration-cap' | 'cancelled';
8
+ }
9
+ /** Positive scene-space distance; the direction supplies its sign. */
10
+ export interface PrototypeDragConfiguration {
11
+ mode: 'scrub';
12
+ axis: 'x' | 'y';
13
+ direction: 1 | -1;
14
+ distance: number;
15
+ commitThreshold: number;
16
+ }
17
+ export interface PrototypeDragPoint {
18
+ x: number;
19
+ y: number;
20
+ }
21
+ export interface PrototypeDragAddress {
22
+ path: readonly string[];
23
+ occurrence?: PrototypeOccurrence;
24
+ }
25
+ /** A finite physical oscillator. Time supplied to its sampler is in milliseconds. */
26
+ export interface PrototypeSpringParameters {
27
+ mass: number;
28
+ stiffness: number;
29
+ damping: number;
30
+ }
@@ -0,0 +1,28 @@
1
+ import type { PrototypeEasing } from './prototype.js';
2
+ import type { PrototypeMotionSample, PrototypeSpringParameters } from './prototypeMotionContracts.js';
3
+ export interface PrototypeSpringPlan {
4
+ readonly from: number;
5
+ readonly to: number;
6
+ /** Absolute progress units per second, including when to < from. */
7
+ readonly initialVelocity: number;
8
+ readonly parameters: Readonly<PrototypeSpringParameters>;
9
+ readonly maxDurationMs: number;
10
+ readonly durationMs: number;
11
+ readonly reason: 'settled' | 'duration-cap';
12
+ }
13
+ export interface CreatePrototypeSpringPlanOptions {
14
+ parameters: PrototypeSpringParameters;
15
+ from?: number;
16
+ to?: number;
17
+ initialVelocity?: number;
18
+ /** Maximum physical time, bounded to ten seconds; never a trajectory scaling factor. */
19
+ durationMs: number;
20
+ }
21
+ export declare function resolvePrototypeSpringParameters(easing: Extract<PrototypeEasing, {
22
+ type: 'spring';
23
+ }>): PrototypeSpringParameters;
24
+ export declare function createPrototypeSpringPlan(input: CreatePrototypeSpringPlanOptions): PrototypeSpringPlan;
25
+ export declare function samplePrototypeSpring(plan: PrototypeSpringPlan, elapsedMs: number): PrototypeMotionSample;
26
+ /** Bounded piecewise-linear CSS approximation of physical samples, including overshoot and truncated endpoints.
27
+ * The last value is never forced to one. Equal endpoints with nonzero velocity need actual sampled keyframes. */
28
+ export declare function prototypeSpringEasingCss(plan: PrototypeSpringPlan, sampleCount?: number): string;
@@ -10,6 +10,10 @@ export type { StylesPanelProps } from './workspace/edit/StylesPanel.js';
10
10
  export { TokenLibraryProvider, useTokenLibraryController, useHasTokenLibraryProvider } from './workspace/tokens/TokenLibraryContext.js';
11
11
  export { createTokenLibraryEngine, reopenTokenLibrary, validateTokenLibraryDocument, resolveTokenLibraryValue, tokenValueEditability, TokenLibraryValidationError } from './token-library/engine.js';
12
12
  export type * from './token-library/contracts.js';
13
+ export { createTokenLibraryCache } from './token-library/cache.js';
14
+ export type { TokenLibraryTextResolver, TokenLibraryPreviewContext, TokenLibraryCacheOptions, TokenLibraryCache } from './token-library/cache.js';
15
+ export { formatTokenReferences, TokenReferenceFormatError } from './token-library/formatReferences.js';
16
+ export type { TokenReferenceFormatContext } from './token-library/formatReferences.js';
13
17
  export { createTokenValueResolver } from './token-library/valueResolver.js';
14
18
  export type { TokenValueReferenceRange, TokenValueResolverOptions, ResolvedTokenText } from './token-library/valueResolver.js';
15
19
  export type * from './workspace/tokens/contracts.js';
@@ -419,10 +423,16 @@ export * from './workspace/edit/clipboard.js';
419
423
  export { AssetsPanel } from './workspace/edit/AssetsPanel.js';
420
424
  export type { AssetsPanelProps } from './workspace/edit/AssetsPanel.js';
421
425
  export * from './document/prototype.js';
426
+ export * from './document/prototypeMotionContracts.js';
427
+ export * from './document/prototypeDrag.js';
428
+ export * from './document/prototypeSpring.js';
429
+ export * from './document/prototypeEasing.js';
430
+ export * from './document/prototypeMotion.js';
422
431
  export { remapPrototypeProperties } from './document/prototypeReferences.js';
423
432
  export { PrototypePanel } from './workspace/edit/PrototypePanel.js';
424
433
  export { PrototypePlayer } from './workspace/view/PrototypePlayer.js';
425
434
  export type { PrototypePlayerProps } from './workspace/view/PrototypePlayer.js';
426
435
  export * from './workspace/view/prototypePlayback.js';
436
+ export type { PrototypeInputFrame } from './workspace/view/prototypeInputFrame.js';
427
437
  export { createPrototypeProjectionContext } from './workspace/edit/prototypeProjection.js';
428
438
  export type { PrototypeProjectionContext } from './workspace/edit/prototypeProjection.js';
@@ -0,0 +1,20 @@
1
+ import type { TokenLibraryDocument } from './contracts.js';
2
+ import type { ResolvedTokenText } from './valueResolver.js';
3
+ export type TokenLibraryTextResolver = (tokenId: string, modeId: string) => ResolvedTokenText;
4
+ export interface TokenLibraryPreviewContext {
5
+ document: TokenLibraryDocument;
6
+ modeId: string;
7
+ resolve: TokenLibraryTextResolver;
8
+ }
9
+ export interface TokenLibraryCacheOptions<Preview> {
10
+ createResolver(document: TokenLibraryDocument): TokenLibraryTextResolver;
11
+ createPreview(context: TokenLibraryPreviewContext): Preview;
12
+ }
13
+ export interface TokenLibraryCache<Preview> {
14
+ resolver(document: TokenLibraryDocument): TokenLibraryTextResolver;
15
+ preview(document: TokenLibraryDocument, modeId: string): Preview;
16
+ /** Invalidate callbacks' external baseline inputs; previously returned resolvers remain usable. */
17
+ clear(): void;
18
+ }
19
+ /** Cache immutable document snapshots by object identity without retaining discarded snapshots. */
20
+ export declare function createTokenLibraryCache<Preview>(options: TokenLibraryCacheOptions<Preview>): TokenLibraryCache<Preview>;
@@ -0,0 +1,12 @@
1
+ import type { TokenReferenceSpan, TokenValue } from './contracts.js';
2
+ export interface TokenReferenceFormatContext {
3
+ readonly reference: Readonly<TokenReferenceSpan>;
4
+ /** The exact original UTF-16 slice, unaffected by replacements of other spans. */
5
+ readonly original: string;
6
+ }
7
+ export declare class TokenReferenceFormatError extends Error {
8
+ readonly code: 'invalid-range' | 'invalid-replacement';
9
+ constructor(code: TokenReferenceFormatError['code'], message: string);
10
+ }
11
+ /** Validate every span before invoking a syntax-specific replacement callback. */
12
+ export declare function formatTokenReferences(value: Pick<TokenValue, 'text' | 'references'>, replace: (context: TokenReferenceFormatContext) => string): string;
@@ -0,0 +1,7 @@
1
+ import type { EditorDeclarations } from '../editor/declarations.js';
2
+
3
+ export interface WorkspaceConditionsProps {
4
+ declarations: Pick<EditorDeclarations, 'getSessionPresets' | 'getSelectedSessionPreset' | 'selectSessionPreset' | 'subscribeSessionPresets' | 'getPreviewEnvironmentControls' | 'getPreviewEnvironmentControlValue' | 'getPreviewEnvironmentControlSelections' | 'setPreviewEnvironmentControlValue' | 'clearPreviewEnvironmentControlValues' | 'subscribePreviewEnvironmentControls' | 'getPreviewEnvironmentControlRevision'>;
5
+ }
6
+ /** Present declared catalog sessions and preview values without interpreting product policy. */
7
+ export declare function WorkspaceConditions({ declarations }: WorkspaceConditionsProps): import("react").JSX.Element | null;
@@ -1,4 +1,4 @@
1
- import type { WorkspaceConfiguration } from './configuration.js';
1
+ import { type WorkspaceConfiguration } from './configuration.js';
2
2
 
3
3
  export interface WorkspaceShellProps {
4
4
  configuration: WorkspaceConfiguration;
@@ -4,6 +4,8 @@ import type { DesignDocument, DocumentNode, DocumentResolveOptions } from '../..
4
4
  export interface ProjectedDocumentNode {
5
5
  sourceNodeId: string;
6
6
  instanceId?: string;
7
+ /** Runtime presentation may attach the canonical enclosing instance path. */
8
+ instancePath?: readonly string[];
7
9
  rowPath: BindingRuntimeScope['rowPath'];
8
10
  node: DocumentNode;
9
11
  children: ProjectedDocumentNode[];
@@ -1,3 +1,5 @@
1
- export declare const StoryboardWorkspace: import("react").FunctionComponent<{
1
+ export interface StoryboardWorkspaceProps {
2
2
  sourceRevision: string;
3
- }>;
3
+ canEditSelected: boolean;
4
+ }
5
+ export declare const StoryboardWorkspace: import("react").FunctionComponent<StoryboardWorkspaceProps>;
@@ -0,0 +1,13 @@
1
+ /** Frame scheduling only. Input timestamps continue to come from the playback clock. */
2
+ export interface PrototypeInputFrame {
3
+ request(callback: () => void): unknown;
4
+ cancel(handle: unknown): void;
5
+ }
6
+ /** Retain one latest value and publish it at most once per requested frame.
7
+ * Flush is for an owned release; cancel discards input on lifecycle changes.
8
+ * Generation checks also reject callbacks delivered after scheduler cancellation. */
9
+ export declare function createPrototypeInputFrame<T>(frame: PrototypeInputFrame, publish: (value: T) => void): {
10
+ push(value: T): void;
11
+ flush: () => void;
12
+ cancel: () => void;
13
+ };
@@ -0,0 +1,5 @@
1
+ import type { PrototypeMotionLayer } from '../../document/prototypeMotion.js';
2
+ /** Collect both incoming and inert outgoing scenes without traversing outside the supplied scene.
3
+ * Legacy renderers can recover kinds from their source document. Unresolved or malformed
4
+ * identities remain invalid descriptors so they crossfade without being paired. */
5
+ export declare function collectPrototypeMotionLayers(scene: HTMLElement, resolveKind?: (sourceNodeId: string) => string | undefined): PrototypeMotionLayer<HTMLElement>[];
@@ -1,5 +1,7 @@
1
1
  import type { DesignDocument, DocumentNode, ResolvedDocumentNode } from '../../document/contracts.js';
2
2
  import { type PrototypeEffect, type PrototypeEvent, type PrototypeRuntimeContext, type PrototypeStartOptions, type PrototypeState } from '../../document/prototype.js';
3
+ import { type PrototypeDragFinishInput, type PrototypeDragStartInput } from '../../document/prototypeDrag.js';
4
+ import { type PrototypeInputFrame } from './prototypeInputFrame.js';
3
5
  export interface PrototypeClock {
4
6
  now(): number;
5
7
  schedule(callback: () => void, delayMs: number): unknown;
@@ -10,9 +12,18 @@ export interface PrototypePlayerSnapshot {
10
12
  previous: PrototypeState | null;
11
13
  effects: readonly PrototypeEffect[];
12
14
  playing: boolean;
15
+ drag: {
16
+ id: number;
17
+ phase: 'dragging' | 'settling';
18
+ progress: number;
19
+ velocity: number;
20
+ outcome: 'commit' | 'cancel' | null;
21
+ } | null;
13
22
  }
14
23
  export interface PrototypePlayerOptions extends PrototypeStartOptions {
15
24
  clock: PrototypeClock;
25
+ /** Optional presentation batching; raw samples still use this player's clock immediately. */
26
+ inputFrame?: PrototypeInputFrame;
16
27
  /** Effects describe intended links and scrolling; the adapter decides how to execute them. */
17
28
  onEffect?(effect: PrototypeEffect): void;
18
29
  }
@@ -20,6 +31,9 @@ export interface PrototypePlayerController {
20
31
  getSnapshot(): PrototypePlayerSnapshot;
21
32
  subscribe(listener: () => void): () => void;
22
33
  dispatch(event: PrototypeEvent): void;
34
+ beginDrag(input: Omit<PrototypeDragStartInput, 'now'>): boolean;
35
+ updateDrag(input: Pick<PrototypeDragStartInput, 'pointerId' | 'point'>): void;
36
+ endDrag(input: Omit<PrototypeDragFinishInput, 'now'>): void;
23
37
  pause(): void;
24
38
  resume(): void;
25
39
  dispose(): void;
@@ -0,0 +1,42 @@
1
+ import type { PageModel } from '../../editor/store.js';
2
+ import type { StoryboardGraph, StoryboardGraphEdge } from '../../editor/storyboardGraph.js';
3
+ import type { WorkspaceCamera } from '../contracts.js';
4
+ export interface StoryboardBounds {
5
+ x: number;
6
+ y: number;
7
+ width: number;
8
+ height: number;
9
+ }
10
+ export interface StoryboardViewport {
11
+ width: number;
12
+ height: number;
13
+ }
14
+ export interface StoryboardArtboard extends StoryboardBounds {
15
+ page: PageModel;
16
+ screenId: string;
17
+ group: string;
18
+ }
19
+ export interface StoryboardConnection {
20
+ edge: StoryboardGraphEdge;
21
+ from: StoryboardArtboard;
22
+ to: StoryboardArtboard;
23
+ path: string;
24
+ labelX: number;
25
+ labelY: number;
26
+ }
27
+ export declare const storyboardFlow: (page: PageModel) => string;
28
+ /** Arrange source-sized screens in flow lanes; never rewrite authored page geometry. */
29
+ export declare function layoutStoryboard(pages: readonly PageModel[]): {
30
+ frames: StoryboardArtboard[];
31
+ bounds: StoryboardBounds;
32
+ };
33
+ export declare function storyboardFrameBounds(frame: StoryboardBounds): StoryboardBounds;
34
+ export declare function fitStoryboard(bounds: StoryboardBounds, viewport: StoryboardViewport): WorkspaceCamera;
35
+ /** Preserve the world point under the pointer when zooming. */
36
+ export declare function zoomStoryboard(camera: WorkspaceCamera, point: {
37
+ x: number;
38
+ y: number;
39
+ }, requested: number): WorkspaceCamera;
40
+ export declare function visibleStoryboardFrames(frames: readonly StoryboardArtboard[], camera: WorkspaceCamera, viewport: StoryboardViewport): StoryboardArtboard[];
41
+ /** A line means an authored transition. Layout proximity never creates an edge. */
42
+ export declare function storyboardConnections(frames: readonly StoryboardArtboard[], graph: StoryboardGraph | null): StoryboardConnection[];
@@ -0,0 +1,26 @@
1
+ import type { EditorRuntime } from '../../core/runtime.js';
2
+ import { type CaptureSupplyDriverDeps } from '../../editor/captureSupply.js';
3
+ import type { RoutePreviewArtifactOutcome } from '../../editor/routePreview.js';
4
+ import type { PageModel } from '../../editor/store.js';
5
+ type Runtime = Pick<EditorRuntime, 'host' | 'previews' | 'declarations'>;
6
+ export interface StoryboardCaptureDemand {
7
+ sourceRevision: string;
8
+ /** Only frames intersecting the canvas viewport, after its catalog filters. */
9
+ pages: readonly PageModel[];
10
+ selected?: PageModel;
11
+ }
12
+ export interface StoryboardCaptureState {
13
+ outcome: RoutePreviewArtifactOutcome | null;
14
+ status: 'missing' | 'generating' | 'ready' | 'stale' | 'failed';
15
+ message?: string;
16
+ retry(): void;
17
+ }
18
+ /** Demand comes from the canvas; transport, deduplication and bounded readiness retries remain shared. */
19
+ export declare function createStoryboardCaptureController(runtime: Runtime, timers?: Pick<CaptureSupplyDriverDeps, 'setTimeout' | 'clearTimeout'>): {
20
+ update: (next: StoryboardCaptureDemand, retryId?: string) => void;
21
+ dispose: () => void;
22
+ subscribe: (listener: () => void) => () => void;
23
+ getSnapshot: () => import("../../editor/captureSupply.js").CaptureSupplyStatus;
24
+ capture(page: PageModel, sourceRevision?: string): StoryboardCaptureState;
25
+ };
26
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { EditorRuntime } from '../../core/runtime.js';
2
+ import type { PageModel } from '../../editor/store.js';
3
+ import { type StoryboardCaptureDemand } from './storyboardCaptures.js';
4
+ /** Canvas visibility is supplied by its layout; repeated renders cannot enqueue another identical plan. */
5
+ export declare function useStoryboardCaptures(runtime: EditorRuntime, demand: StoryboardCaptureDemand): (page: PageModel) => import("./storyboardCaptures.js").StoryboardCaptureState;
@@ -231,6 +231,7 @@ export function createPreviewCaptureWorkerChannel({
231
231
  let degraded = false;
232
232
  let disposed = false;
233
233
  let idleTimer = null;
234
+ let restartWait = null;
234
235
  let requestCounter = 0;
235
236
  const crashTimestamps = [];
236
237
 
@@ -441,14 +442,21 @@ export function createPreviewCaptureWorkerChannel({
441
442
  // The wedged worker was killed and the crash recorded. When that
442
443
  // crash exhausted the restart budget, the caller takes the degraded
443
444
  // path; otherwise fall through to a fresh spawn below.
445
+ if (disposed) throw new Error('Preview capture worker channel is disposed.');
444
446
  if (degraded) return null;
445
447
  }
446
448
  }
447
449
  const backoff = restartBackoffMs();
448
450
  if (backoff > 0) {
449
- await new Promise((resolve) => {
450
- const timer = setTimeout(resolve, backoff);
451
- timer.unref?.();
451
+ await new Promise((resolve, reject) => {
452
+ const pending = { timer: null, reject };
453
+ // There is no child process during backoff. The active capture owns
454
+ // this delay until it completes or dispose cancels the request.
455
+ pending.timer = setTimeout(() => {
456
+ if (restartWait === pending) restartWait = null;
457
+ resolve();
458
+ }, backoff);
459
+ restartWait = pending;
452
460
  });
453
461
  }
454
462
  if (disposed) {
@@ -595,6 +603,12 @@ export function createPreviewCaptureWorkerChannel({
595
603
  if (disposed) return;
596
604
  disposed = true;
597
605
  clearIdleTimer();
606
+ if (restartWait) {
607
+ const pending = restartWait;
608
+ restartWait = null;
609
+ clearTimeout(pending.timer);
610
+ pending.reject(new Error('Preview capture worker channel is disposed.'));
611
+ }
598
612
  await stopWorker();
599
613
  }
600
614
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pygmalionjs/pygmalion",
3
- "version": "0.11.1",
3
+ "version": "0.13.0",
4
4
  "description": "Code-backed DOM design sandbox and visual QA editor",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {