@scenar/core 0.4.0 → 0.6.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.
package/README.md CHANGED
@@ -12,7 +12,7 @@ pnpm add @scenar/core
12
12
 
13
13
  ### Scenario types
14
14
 
15
- - **`ScenarioStep<T>`** — A single step in a scenario timeline with typed data payload, delay, caption, narration text, and inline interactions.
15
+ - **`ScenarioStep<T>`** — A single step in a scenario timeline with typed data payload, delay, narration text, and inline interactions.
16
16
  - **`StepAction`** — A timed interaction within a step (click, type, hover, drag, scroll_to, set_cursor, clear_cursor, viewport_transition).
17
17
 
18
18
  ### Timeline computation
package/index.d.ts CHANGED
@@ -2,15 +2,15 @@ export type { ScenarioStep } from "./scenario/types.js";
2
2
  export type { ScenarioBundle } from "./scenario/bundle.js";
3
3
  export type { ActionType, StepAction, UseStepInteractionsOptions, } from "./scenario/step-action.js";
4
4
  export type { NarrationEntry, NarrationManifest } from "./narration/types.js";
5
- export type { ViewportTransform } from "./viewport/transform.js";
6
- export { VIEWPORT_TRANSFORM_IDENTITY } from "./viewport/transform.js";
5
+ export type { ViewportTransform, ViewportCameraMove } from "./viewport/transform.js";
6
+ export { VIEWPORT_TRANSFORM_IDENTITY, VIEWPORT_CAMERA_AT_REST, cameraEase, interpolateViewportTransform, } from "./viewport/transform.js";
7
7
  export type { Position } from "./cursor/compute-position.js";
8
8
  export { computeCursorPosition } from "./cursor/compute-position.js";
9
9
  export type { StepTimeline } from "./timeline/compute-step-timeline.js";
10
10
  export { computeStepTimeline } from "./timeline/compute-step-timeline.js";
11
11
  export { deriveStepFromTime } from "./timeline/derive-step.js";
12
12
  export { getStepDurationMs } from "./timeline/step-duration.js";
13
- export { CLICK_DELAY_MS, TYPE_CHAR_DELAY_MS, HOVER_HOLD_MS, DRAG_SETTLE_MS, VIEWPORT_SETTLE_MS, } from "./timing/constants.js";
13
+ export { CLICK_DELAY_MS, TYPE_CHAR_DELAY_MS, HOVER_HOLD_MS, DRAG_SETTLE_MS, CAMERA_TRANSITION_MS, VIEWPORT_SETTLE_MS, } from "./timing/constants.js";
14
14
  export { findScrollParent, scrollTargetIntoView, scrollTargetIntoViewInstant, } from "./dom/scroll.js";
15
15
  export { CURSOR_TARGET_ATTRIBUTE, SCROLL_TARGET_ATTRIBUTE, HOVER_STATE_ATTRIBUTE, DRAG_STATE_ATTRIBUTE, cursorTargetSelector, scrollTargetSelector, } from "./targeting/data-attributes.js";
16
16
  export { SCENAR_EMBED_SOURCE, SCENAR_EMBED_PROTOCOL_VERSION, frameEmbedEvent, frameEmbedCommand, parseEmbedCommand, parseEmbedEvent, } from "./embed/protocol.js";
package/index.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,YAAY,EACV,UAAU,EACV,UAAU,EACV,0BAA0B,GAC3B,MAAM,2BAA2B,CAAC;AAGnC,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAG9E,YAAY,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAGtE,YAAY,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,YAAY,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAGhE,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EACL,mBAAmB,EACnB,6BAA6B,EAC7B,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,YAAY,EACV,yBAAyB,EACzB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,YAAY,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,YAAY,EACV,UAAU,EACV,UAAU,EACV,0BAA0B,GAC3B,MAAM,2BAA2B,CAAC;AAGnC,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAG9E,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,UAAU,EACV,4BAA4B,GAC7B,MAAM,yBAAyB,CAAC;AAGjC,YAAY,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,YAAY,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AACxE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAGhE,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EACL,mBAAmB,EACnB,6BAA6B,EAC7B,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,GAC1B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC;AACvE,YAAY,EACV,yBAAyB,EACzB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,4BAA4B,CAAC"}
package/index.js CHANGED
@@ -1,12 +1,12 @@
1
1
  // @scenar/core — public API
2
2
  // Pure TypeScript types, timing, and utilities for scenario playback.
3
- export { VIEWPORT_TRANSFORM_IDENTITY } from "./viewport/transform.js";
3
+ export { VIEWPORT_TRANSFORM_IDENTITY, VIEWPORT_CAMERA_AT_REST, cameraEase, interpolateViewportTransform, } from "./viewport/transform.js";
4
4
  export { computeCursorPosition } from "./cursor/compute-position.js";
5
5
  export { computeStepTimeline } from "./timeline/compute-step-timeline.js";
6
6
  export { deriveStepFromTime } from "./timeline/derive-step.js";
7
7
  export { getStepDurationMs } from "./timeline/step-duration.js";
8
8
  // Timing constants
9
- export { CLICK_DELAY_MS, TYPE_CHAR_DELAY_MS, HOVER_HOLD_MS, DRAG_SETTLE_MS, VIEWPORT_SETTLE_MS, } from "./timing/constants.js";
9
+ export { CLICK_DELAY_MS, TYPE_CHAR_DELAY_MS, HOVER_HOLD_MS, DRAG_SETTLE_MS, CAMERA_TRANSITION_MS, VIEWPORT_SETTLE_MS, } from "./timing/constants.js";
10
10
  // DOM scroll utilities
11
11
  export { findScrollParent, scrollTargetIntoView, scrollTargetIntoViewInstant, } from "./dom/scroll.js";
12
12
  // Data-attribute targeting contract
package/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,sEAAsE;AAgBtE,OAAO,EAAE,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AAItE,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAIrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAEhE,mBAAmB;AACnB,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAE/B,uBAAuB;AACvB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAEzB,oCAAoC;AACpC,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,gCAAgC,CAAC;AAExC,0EAA0E;AAC1E,OAAO,EACL,mBAAmB,EACnB,6BAA6B,EAC7B,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAQ7B,uEAAuE;AACvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,4BAA4B;AAC5B,sEAAsE;AAgBtE,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,UAAU,EACV,4BAA4B,GAC7B,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAIrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAEhE,mBAAmB;AACnB,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAE/B,uBAAuB;AACvB,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AAEzB,oCAAoC;AACpC,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,gCAAgC,CAAC;AAExC,0EAA0E;AAC1E,OAAO,EACL,mBAAmB,EACnB,6BAA6B,EAC7B,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,eAAe,GAChB,MAAM,qBAAqB,CAAC;AAQ7B,uEAAuE;AACvE,OAAO,EAAE,yBAAyB,EAAE,MAAM,4BAA4B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scenar/core",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Pure TypeScript types, timing, and utilities for the Scenar scenario engine.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -101,5 +101,15 @@ export interface UseStepInteractionsOptions<T> {
101
101
  * needed to center the target element and calls this callback.
102
102
  */
103
103
  setViewportTransform?: (transform: ViewportTransform) => void;
104
+ /**
105
+ * The camera's content element (`ViewportTransformLayer`'s `contentRef`).
106
+ * `viewport_transition` target math measures against it so canonical
107
+ * coordinates stay correct at any camera state. Optional — effects fall
108
+ * back to `containerRef`, which is only exact while the camera is at
109
+ * identity.
110
+ */
111
+ cameraRef?: {
112
+ current: HTMLElement | null;
113
+ };
104
114
  }
105
115
  //# sourceMappingURL=step-action.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"step-action.d.ts","sourceRoot":"","sources":["../../src/scenario/step-action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAClB,WAAW,GACX,YAAY,GACZ,cAAc,GACd,OAAO,GACP,MAAM,GACN,OAAO,GACP,MAAM,GACN,qBAAqB,CAAC;AAE1B,gDAAgD;AAChD,MAAM,WAAW,UAAU;IACzB;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,mBAAmB;IACnB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,wEAAwE;IACxE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,sDAAsD;AACtD,MAAM,WAAW,0BAA0B,CAAC,CAAC;IAC3C,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,iBAAiB,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACjD,qCAAqC;IACrC,YAAY,EAAE;QAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;KAAE,CAAC;IAC9C,qDAAqD;IACrD,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACtD;;;;OAIG;IACH,KAAK,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IAClC;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1C;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC/D"}
1
+ {"version":3,"file":"step-action.d.ts","sourceRoot":"","sources":["../../src/scenario/step-action.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAClB,WAAW,GACX,YAAY,GACZ,cAAc,GACd,OAAO,GACP,MAAM,GACN,OAAO,GACP,MAAM,GACN,qBAAqB,CAAC;AAE1B,gDAAgD;AAChD,MAAM,WAAW,UAAU;IACzB;;;;;;;OAOG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,mBAAmB;IACnB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,wEAAwE;IACxE,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,sDAAsD;AACtD,MAAM,WAAW,0BAA0B,CAAC,CAAC;IAC3C,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,iBAAiB,EAAE,iBAAiB,GAAG,SAAS,CAAC;IACjD,qCAAqC;IACrC,YAAY,EAAE;QAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;KAAE,CAAC;IAC9C,qDAAqD;IACrD,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IACtD;;;;OAIG;IACH,KAAK,EAAE,SAAS,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC;IAClC;;;;OAIG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC;;;;OAIG;IACH,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1C;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,CAAC,SAAS,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC9D;;;;;;OAMG;IACH,SAAS,CAAC,EAAE;QAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAAA;KAAE,CAAC;CAC7C"}
@@ -15,8 +15,6 @@ export interface ScenarioStep<T> {
15
15
  readonly delayMs: number;
16
16
  /** Data snapshot at this point in the timeline. */
17
17
  readonly data: T;
18
- /** Short label shown below the demo content describing the current action. */
19
- readonly caption?: string;
20
18
  /**
21
19
  * Narration script for TTS generation. Consumed by the build script
22
20
  * to produce audio files — not rendered at runtime.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scenario/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;GAIG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;OAOG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;CAC/C"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/scenario/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;GAIG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;OAOG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;CAC/C"}
package/src/index.ts CHANGED
@@ -14,8 +14,13 @@ export type {
14
14
  export type { NarrationEntry, NarrationManifest } from "./narration/types.js";
15
15
 
16
16
  // Viewport types
17
- export type { ViewportTransform } from "./viewport/transform.js";
18
- export { VIEWPORT_TRANSFORM_IDENTITY } from "./viewport/transform.js";
17
+ export type { ViewportTransform, ViewportCameraMove } from "./viewport/transform.js";
18
+ export {
19
+ VIEWPORT_TRANSFORM_IDENTITY,
20
+ VIEWPORT_CAMERA_AT_REST,
21
+ cameraEase,
22
+ interpolateViewportTransform,
23
+ } from "./viewport/transform.js";
19
24
 
20
25
  // Cursor types
21
26
  export type { Position } from "./cursor/compute-position.js";
@@ -33,6 +38,7 @@ export {
33
38
  TYPE_CHAR_DELAY_MS,
34
39
  HOVER_HOLD_MS,
35
40
  DRAG_SETTLE_MS,
41
+ CAMERA_TRANSITION_MS,
36
42
  VIEWPORT_SETTLE_MS,
37
43
  } from "./timing/constants.js";
38
44
 
@@ -110,4 +110,12 @@ export interface UseStepInteractionsOptions<T> {
110
110
  * needed to center the target element and calls this callback.
111
111
  */
112
112
  setViewportTransform?: (transform: ViewportTransform) => void;
113
+ /**
114
+ * The camera's content element (`ViewportTransformLayer`'s `contentRef`).
115
+ * `viewport_transition` target math measures against it so canonical
116
+ * coordinates stay correct at any camera state. Optional — effects fall
117
+ * back to `containerRef`, which is only exact while the camera is at
118
+ * identity.
119
+ */
120
+ cameraRef?: { current: HTMLElement | null };
113
121
  }
@@ -17,8 +17,6 @@ export interface ScenarioStep<T> {
17
17
  readonly delayMs: number;
18
18
  /** Data snapshot at this point in the timeline. */
19
19
  readonly data: T;
20
- /** Short label shown below the demo content describing the current action. */
21
- readonly caption?: string;
22
20
  /**
23
21
  * Narration script for TTS generation. Consumed by the build script
24
22
  * to produce audio files — not rendered at runtime.
@@ -11,9 +11,9 @@
11
11
  * Milliseconds after a cursor target is set before the cursor is
12
12
  * considered "arrived" and the click ripple appears.
13
13
  *
14
- * Derived from the Cursor spring parameters (stiffness 170,
15
- * damping 22, mass 0.6) the spring visually settles within
16
- * this window for typical travel distances in the viewport.
14
+ * The Cursor's distance-adaptive travel clamps its longest move to
15
+ * settle inside this window (see `travelTransition` in
16
+ * `@scenar/react`'s Cursor) the ripple must never fire mid-flight.
17
17
  */
18
18
  export const CLICK_DELAY_MS = 450;
19
19
 
@@ -48,12 +48,19 @@ export const HOVER_HOLD_MS = 1500;
48
48
  export const DRAG_SETTLE_MS = 200;
49
49
 
50
50
  /**
51
- * Expected milliseconds for the viewport transition spring to settle
52
- * after a zoom or pan change.
51
+ * Duration (ms) of a camera move a `viewport_transition` zoom or pan.
53
52
  *
54
- * The ViewportTransformLayer uses a softer spring (stiffness 100,
55
- * damping 20, mass 0.8) than the cursor spring, so it takes longer
56
- * to settle. Used for dev-mode warnings when a subsequent cursor
57
- * action is scheduled too close to a viewport transition.
53
+ * This is a fixed tween, not a spring: browser playback eases toward the
54
+ * target with `cameraEase` over exactly this duration, and video export
55
+ * computes the same curve per frame via `interpolateViewportTransform`,
56
+ * so both paths agree at every instant.
58
57
  */
59
- export const VIEWPORT_SETTLE_MS = 500;
58
+ export const CAMERA_TRANSITION_MS = 600;
59
+
60
+ /**
61
+ * Milliseconds after a `viewport_transition` fires before the camera is
62
+ * at rest. Equal to {@link CAMERA_TRANSITION_MS} (the move is a fixed
63
+ * tween). Used for dev-mode warnings when a subsequent cursor action is
64
+ * scheduled too close to a viewport transition.
65
+ */
66
+ export const VIEWPORT_SETTLE_MS = CAMERA_TRANSITION_MS;
@@ -0,0 +1,81 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import {
3
+ VIEWPORT_CAMERA_AT_REST,
4
+ VIEWPORT_TRANSFORM_IDENTITY,
5
+ cameraEase,
6
+ interpolateViewportTransform,
7
+ } from "./transform.js";
8
+
9
+ describe("cameraEase", () => {
10
+ it("starts at 0 and ends at 1", () => {
11
+ expect(cameraEase(0)).toBe(0);
12
+ expect(cameraEase(1)).toBe(1);
13
+ });
14
+
15
+ it("clamps out-of-range progress", () => {
16
+ expect(cameraEase(-0.5)).toBe(0);
17
+ expect(cameraEase(1.5)).toBe(1);
18
+ });
19
+
20
+ it("is monotonically non-decreasing", () => {
21
+ let prev = cameraEase(0);
22
+ for (let i = 1; i <= 100; i++) {
23
+ const next = cameraEase(i / 100);
24
+ expect(next).toBeGreaterThanOrEqual(prev);
25
+ prev = next;
26
+ }
27
+ });
28
+
29
+ it("decelerates: covers more than half the distance in the first quarter", () => {
30
+ // The defining property of a camera move — commit fast, glide to rest.
31
+ expect(cameraEase(0.25)).toBeGreaterThan(0.5);
32
+ });
33
+
34
+ it("never overshoots", () => {
35
+ for (let i = 0; i <= 100; i++) {
36
+ expect(cameraEase(i / 100)).toBeLessThanOrEqual(1);
37
+ }
38
+ });
39
+ });
40
+
41
+ describe("interpolateViewportTransform", () => {
42
+ const from = { scale: 1, x: 0, y: 0 };
43
+ const to = { scale: 1.5, x: -200, y: -120 };
44
+
45
+ it("returns `from` at progress 0", () => {
46
+ expect(interpolateViewportTransform(from, to, 0)).toEqual(from);
47
+ });
48
+
49
+ it("returns `to` at progress 1", () => {
50
+ expect(interpolateViewportTransform(from, to, 1)).toEqual(to);
51
+ });
52
+
53
+ it("clamps beyond-end progress to `to` (the camera rests after the move)", () => {
54
+ expect(interpolateViewportTransform(from, to, 42)).toEqual(to);
55
+ });
56
+
57
+ it("applies the ease to every component consistently", () => {
58
+ const eased = cameraEase(0.5);
59
+ const mid = interpolateViewportTransform(from, to, 0.5);
60
+ expect(mid.scale).toBeCloseTo(1 + 0.5 * eased, 10);
61
+ expect(mid.x).toBeCloseTo(-200 * eased, 10);
62
+ expect(mid.y).toBeCloseTo(-120 * eased, 10);
63
+ });
64
+
65
+ it("is an identity when from and to are equal", () => {
66
+ for (const p of [0, 0.3, 0.7, 1]) {
67
+ expect(
68
+ interpolateViewportTransform(
69
+ VIEWPORT_TRANSFORM_IDENTITY,
70
+ VIEWPORT_TRANSFORM_IDENTITY,
71
+ p,
72
+ ),
73
+ ).toEqual(VIEWPORT_TRANSFORM_IDENTITY);
74
+ }
75
+ });
76
+
77
+ it("VIEWPORT_CAMERA_AT_REST holds the identity at any time", () => {
78
+ const { from: f, to: t } = VIEWPORT_CAMERA_AT_REST;
79
+ expect(interpolateViewportTransform(f, t, 0.5)).toEqual(VIEWPORT_TRANSFORM_IDENTITY);
80
+ });
81
+ });
@@ -11,3 +11,65 @@ export const VIEWPORT_TRANSFORM_IDENTITY: Readonly<ViewportTransform> = {
11
11
  x: 0,
12
12
  y: 0,
13
13
  };
14
+
15
+ /**
16
+ * A camera move: the transform it started `from`, the transform it is
17
+ * heading `to`, and the timeline instant the move fired at.
18
+ *
19
+ * Both output paths derive the same visual from this one value: browser
20
+ * playback tweens toward `to` with {@link cameraEase} over
21
+ * {@link CAMERA_TRANSITION_MS}; video export computes the exact
22
+ * intermediate transform for any frame with
23
+ * {@link interpolateViewportTransform} — same curve, same duration,
24
+ * zero drift.
25
+ */
26
+ export interface ViewportCameraMove {
27
+ readonly from: ViewportTransform;
28
+ readonly to: ViewportTransform;
29
+ /** Timeline time (ms) the move fired at, in the scenario's time domain. */
30
+ readonly atTimeMs: number;
31
+ }
32
+
33
+ /** A camera resting at the identity transform since t=0. */
34
+ export const VIEWPORT_CAMERA_AT_REST: Readonly<ViewportCameraMove> = {
35
+ from: VIEWPORT_TRANSFORM_IDENTITY,
36
+ to: VIEWPORT_TRANSFORM_IDENTITY,
37
+ atTimeMs: 0,
38
+ };
39
+
40
+ /**
41
+ * The camera's deceleration curve (ease-out quint).
42
+ *
43
+ * A camera move commits fast and glides to a stop — it must never
44
+ * overshoot, because overshoot reads as a zoom *effect* rather than a
45
+ * camera. Referentially transparent so browser playback (as a tween
46
+ * easing) and video export (via {@link interpolateViewportTransform})
47
+ * share one implementation.
48
+ */
49
+ export function cameraEase(progress: number): number {
50
+ const t = Math.min(1, Math.max(0, progress));
51
+ return 1 - (1 - t) ** 5;
52
+ }
53
+
54
+ /**
55
+ * The intermediate transform of a camera move at linear progress
56
+ * `progress` (0..1, clamped). Applies {@link cameraEase}, then
57
+ * interpolates each component. Pure — the video-export path calls this
58
+ * per frame.
59
+ */
60
+ export function interpolateViewportTransform(
61
+ from: ViewportTransform,
62
+ to: ViewportTransform,
63
+ progress: number,
64
+ ): ViewportTransform {
65
+ // Exact endpoint, not `from + (to - from) * 1`: floating point does not
66
+ // guarantee that expression returns `to`, and a camera at rest must land
67
+ // on the authored transform bit-for-bit (consumers compare identity).
68
+ if (progress >= 1) return { ...to };
69
+ const eased = cameraEase(progress);
70
+ return {
71
+ scale: from.scale + (to.scale - from.scale) * eased,
72
+ x: from.x + (to.x - from.x) * eased,
73
+ y: from.y + (to.y - from.y) * eased,
74
+ };
75
+ }
@@ -10,9 +10,9 @@
10
10
  * Milliseconds after a cursor target is set before the cursor is
11
11
  * considered "arrived" and the click ripple appears.
12
12
  *
13
- * Derived from the Cursor spring parameters (stiffness 170,
14
- * damping 22, mass 0.6) the spring visually settles within
15
- * this window for typical travel distances in the viewport.
13
+ * The Cursor's distance-adaptive travel clamps its longest move to
14
+ * settle inside this window (see `travelTransition` in
15
+ * `@scenar/react`'s Cursor) the ripple must never fire mid-flight.
16
16
  */
17
17
  export declare const CLICK_DELAY_MS = 450;
18
18
  /**
@@ -43,13 +43,19 @@ export declare const HOVER_HOLD_MS = 1500;
43
43
  */
44
44
  export declare const DRAG_SETTLE_MS = 200;
45
45
  /**
46
- * Expected milliseconds for the viewport transition spring to settle
47
- * after a zoom or pan change.
46
+ * Duration (ms) of a camera move a `viewport_transition` zoom or pan.
48
47
  *
49
- * The ViewportTransformLayer uses a softer spring (stiffness 100,
50
- * damping 20, mass 0.8) than the cursor spring, so it takes longer
51
- * to settle. Used for dev-mode warnings when a subsequent cursor
52
- * action is scheduled too close to a viewport transition.
48
+ * This is a fixed tween, not a spring: browser playback eases toward the
49
+ * target with `cameraEase` over exactly this duration, and video export
50
+ * computes the same curve per frame via `interpolateViewportTransform`,
51
+ * so both paths agree at every instant.
53
52
  */
54
- export declare const VIEWPORT_SETTLE_MS = 500;
53
+ export declare const CAMERA_TRANSITION_MS = 600;
54
+ /**
55
+ * Milliseconds after a `viewport_transition` fires before the camera is
56
+ * at rest. Equal to {@link CAMERA_TRANSITION_MS} (the move is a fixed
57
+ * tween). Used for dev-mode warnings when a subsequent cursor action is
58
+ * scheduled too close to a viewport transition.
59
+ */
60
+ export declare const VIEWPORT_SETTLE_MS = 600;
55
61
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/timing/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,OAAO,CAAC;AAElC;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,MAAM,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/timing/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,OAAO,CAAC;AAElC;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,MAAuB,CAAC"}
@@ -10,9 +10,9 @@
10
10
  * Milliseconds after a cursor target is set before the cursor is
11
11
  * considered "arrived" and the click ripple appears.
12
12
  *
13
- * Derived from the Cursor spring parameters (stiffness 170,
14
- * damping 22, mass 0.6) the spring visually settles within
15
- * this window for typical travel distances in the viewport.
13
+ * The Cursor's distance-adaptive travel clamps its longest move to
14
+ * settle inside this window (see `travelTransition` in
15
+ * `@scenar/react`'s Cursor) the ripple must never fire mid-flight.
16
16
  */
17
17
  export const CLICK_DELAY_MS = 450;
18
18
  /**
@@ -43,13 +43,19 @@ export const HOVER_HOLD_MS = 1500;
43
43
  */
44
44
  export const DRAG_SETTLE_MS = 200;
45
45
  /**
46
- * Expected milliseconds for the viewport transition spring to settle
47
- * after a zoom or pan change.
46
+ * Duration (ms) of a camera move a `viewport_transition` zoom or pan.
48
47
  *
49
- * The ViewportTransformLayer uses a softer spring (stiffness 100,
50
- * damping 20, mass 0.8) than the cursor spring, so it takes longer
51
- * to settle. Used for dev-mode warnings when a subsequent cursor
52
- * action is scheduled too close to a viewport transition.
48
+ * This is a fixed tween, not a spring: browser playback eases toward the
49
+ * target with `cameraEase` over exactly this duration, and video export
50
+ * computes the same curve per frame via `interpolateViewportTransform`,
51
+ * so both paths agree at every instant.
53
52
  */
54
- export const VIEWPORT_SETTLE_MS = 500;
53
+ export const CAMERA_TRANSITION_MS = 600;
54
+ /**
55
+ * Milliseconds after a `viewport_transition` fires before the camera is
56
+ * at rest. Equal to {@link CAMERA_TRANSITION_MS} (the move is a fixed
57
+ * tween). Used for dev-mode warnings when a subsequent cursor action is
58
+ * scheduled too close to a viewport transition.
59
+ */
60
+ export const VIEWPORT_SETTLE_MS = CAMERA_TRANSITION_MS;
55
61
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/timing/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC;AAElC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAErC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC;AAElC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC;AAElC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAG,CAAC"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/timing/constants.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC;AAElC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAC;AAErC;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,IAAI,CAAC;AAElC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC;AAElC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,CAAC;AAExC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,oBAAoB,CAAC"}
@@ -6,4 +6,40 @@ export interface ViewportTransform {
6
6
  }
7
7
  /** The identity transform — no zoom, no translation. */
8
8
  export declare const VIEWPORT_TRANSFORM_IDENTITY: Readonly<ViewportTransform>;
9
+ /**
10
+ * A camera move: the transform it started `from`, the transform it is
11
+ * heading `to`, and the timeline instant the move fired at.
12
+ *
13
+ * Both output paths derive the same visual from this one value: browser
14
+ * playback tweens toward `to` with {@link cameraEase} over
15
+ * {@link CAMERA_TRANSITION_MS}; video export computes the exact
16
+ * intermediate transform for any frame with
17
+ * {@link interpolateViewportTransform} — same curve, same duration,
18
+ * zero drift.
19
+ */
20
+ export interface ViewportCameraMove {
21
+ readonly from: ViewportTransform;
22
+ readonly to: ViewportTransform;
23
+ /** Timeline time (ms) the move fired at, in the scenario's time domain. */
24
+ readonly atTimeMs: number;
25
+ }
26
+ /** A camera resting at the identity transform since t=0. */
27
+ export declare const VIEWPORT_CAMERA_AT_REST: Readonly<ViewportCameraMove>;
28
+ /**
29
+ * The camera's deceleration curve (ease-out quint).
30
+ *
31
+ * A camera move commits fast and glides to a stop — it must never
32
+ * overshoot, because overshoot reads as a zoom *effect* rather than a
33
+ * camera. Referentially transparent so browser playback (as a tween
34
+ * easing) and video export (via {@link interpolateViewportTransform})
35
+ * share one implementation.
36
+ */
37
+ export declare function cameraEase(progress: number): number;
38
+ /**
39
+ * The intermediate transform of a camera move at linear progress
40
+ * `progress` (0..1, clamped). Applies {@link cameraEase}, then
41
+ * interpolates each component. Pure — the video-export path calls this
42
+ * per frame.
43
+ */
44
+ export declare function interpolateViewportTransform(from: ViewportTransform, to: ViewportTransform, progress: number): ViewportTransform;
9
45
  //# sourceMappingURL=transform.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../src/viewport/transform.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,wDAAwD;AACxD,eAAO,MAAM,2BAA2B,EAAE,QAAQ,CAAC,iBAAiB,CAInE,CAAC"}
1
+ {"version":3,"file":"transform.d.ts","sourceRoot":"","sources":["../../src/viewport/transform.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,wDAAwD;AACxD,eAAO,MAAM,2BAA2B,EAAE,QAAQ,CAAC,iBAAiB,CAInE,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;IACjC,QAAQ,CAAC,EAAE,EAAE,iBAAiB,CAAC;IAC/B,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,4DAA4D;AAC5D,eAAO,MAAM,uBAAuB,EAAE,QAAQ,CAAC,kBAAkB,CAIhE,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,iBAAiB,EACvB,EAAE,EAAE,iBAAiB,EACrB,QAAQ,EAAE,MAAM,GACf,iBAAiB,CAWnB"}
@@ -4,4 +4,42 @@ export const VIEWPORT_TRANSFORM_IDENTITY = {
4
4
  x: 0,
5
5
  y: 0,
6
6
  };
7
+ /** A camera resting at the identity transform since t=0. */
8
+ export const VIEWPORT_CAMERA_AT_REST = {
9
+ from: VIEWPORT_TRANSFORM_IDENTITY,
10
+ to: VIEWPORT_TRANSFORM_IDENTITY,
11
+ atTimeMs: 0,
12
+ };
13
+ /**
14
+ * The camera's deceleration curve (ease-out quint).
15
+ *
16
+ * A camera move commits fast and glides to a stop — it must never
17
+ * overshoot, because overshoot reads as a zoom *effect* rather than a
18
+ * camera. Referentially transparent so browser playback (as a tween
19
+ * easing) and video export (via {@link interpolateViewportTransform})
20
+ * share one implementation.
21
+ */
22
+ export function cameraEase(progress) {
23
+ const t = Math.min(1, Math.max(0, progress));
24
+ return 1 - (1 - t) ** 5;
25
+ }
26
+ /**
27
+ * The intermediate transform of a camera move at linear progress
28
+ * `progress` (0..1, clamped). Applies {@link cameraEase}, then
29
+ * interpolates each component. Pure — the video-export path calls this
30
+ * per frame.
31
+ */
32
+ export function interpolateViewportTransform(from, to, progress) {
33
+ // Exact endpoint, not `from + (to - from) * 1`: floating point does not
34
+ // guarantee that expression returns `to`, and a camera at rest must land
35
+ // on the authored transform bit-for-bit (consumers compare identity).
36
+ if (progress >= 1)
37
+ return { ...to };
38
+ const eased = cameraEase(progress);
39
+ return {
40
+ scale: from.scale + (to.scale - from.scale) * eased,
41
+ x: from.x + (to.x - from.x) * eased,
42
+ y: from.y + (to.y - from.y) * eased,
43
+ };
44
+ }
7
45
  //# sourceMappingURL=transform.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"transform.js","sourceRoot":"","sources":["../../src/viewport/transform.ts"],"names":[],"mappings":"AAOA,wDAAwD;AACxD,MAAM,CAAC,MAAM,2BAA2B,GAAgC;IACtE,KAAK,EAAE,CAAC;IACR,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;CACL,CAAC"}
1
+ {"version":3,"file":"transform.js","sourceRoot":"","sources":["../../src/viewport/transform.ts"],"names":[],"mappings":"AAOA,wDAAwD;AACxD,MAAM,CAAC,MAAM,2BAA2B,GAAgC;IACtE,KAAK,EAAE,CAAC;IACR,CAAC,EAAE,CAAC;IACJ,CAAC,EAAE,CAAC;CACL,CAAC;AAoBF,4DAA4D;AAC5D,MAAM,CAAC,MAAM,uBAAuB,GAAiC;IACnE,IAAI,EAAE,2BAA2B;IACjC,EAAE,EAAE,2BAA2B;IAC/B,QAAQ,EAAE,CAAC;CACZ,CAAC;AAEF;;;;;;;;GAQG;AACH,MAAM,UAAU,UAAU,CAAC,QAAgB;IACzC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B,CAC1C,IAAuB,EACvB,EAAqB,EACrB,QAAgB;IAEhB,wEAAwE;IACxE,yEAAyE;IACzE,sEAAsE;IACtE,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACnC,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,KAAK;QACnD,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK;QACnC,CAAC,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK;KACpC,CAAC;AACJ,CAAC"}