@vosjs/studio-core 0.22.0 → 0.23.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/dist/index.d.ts CHANGED
@@ -73,6 +73,14 @@ interface ZoomStyleParams {
73
73
  targetFill: number;
74
74
  minLevel: number;
75
75
  maxLevel: number;
76
+ /**
77
+ * The level a DRAG is followed at (a press that travels before its
78
+ * release: a slider thumb, a scrubber, a thing moved across a canvas).
79
+ * The camera pans WITH the pointer for the whole press (a path follow,
80
+ * not a dead zone), so the level stays mild: the thumb and what it drives
81
+ * both stay in the window.
82
+ */
83
+ dragLevel: number;
76
84
  /** span lead-in before the first click / hold after the last (seconds). */
77
85
  lead: number;
78
86
  hold: number;
@@ -2065,6 +2073,35 @@ declare function smoothCursor(track: CursorTrack, options?: SmoothOptions): Smoo
2065
2073
  * is set aside before clustering; the targets around it keep their zoom.
2066
2074
  */
2067
2075
  declare const DRAG_FIT_LEVEL = 1.15;
2076
+ /**
2077
+ * A press that TRAVELS before its release is a drag (a slider thumb, a
2078
+ * scrubber, a thing moved across a canvas): the pointer's path is the
2079
+ * content, so the camera follows it for the whole press at the style's
2080
+ * dragLevel instead of framing the press as a click. Travel is the farthest
2081
+ * the pointer gets from the press point, as a fraction of the frame width;
2082
+ * a press held still (a long click) stays a click.
2083
+ */
2084
+ declare const DRAG_MIN_TRAVEL_FRAC = 0.015;
2085
+ declare const DRAG_MIN_S = 0.2;
2086
+ /** A press that travelled before its release, in seconds and frame fractions. */
2087
+ interface Drag {
2088
+ /** the press and the release, seconds */
2089
+ t0: number;
2090
+ t1: number;
2091
+ /** the press point, normalized */
2092
+ nx: number;
2093
+ ny: number;
2094
+ /** the pointer's farthest excursion from the press point, frame widths */
2095
+ travel: number;
2096
+ }
2097
+ /**
2098
+ * The drags in a cursor track: each `down` paired with the next `up`, kept
2099
+ * when the pointer travelled ≥ DRAG_MIN_TRAVEL_FRAC between them over
2100
+ * ≥ DRAG_MIN_S. Shared by the planner (a drag plans a follow span, and its
2101
+ * press is not a click) and the lowering's cursor follow (a drag inside any
2102
+ * follow span is tracked as a path).
2103
+ */
2104
+ declare function dragsFromTrack(track: CursorTrack, width: number, height: number): Drag[];
2068
2105
  interface PlanOptions {
2069
2106
  /** captured frame size (for normalizing rects → [0..1] focus points). */
2070
2107
  width: number;
@@ -2088,6 +2125,8 @@ interface PlanOptions {
2088
2125
  hold?: number;
2089
2126
  /** minimum clicks for a cluster to earn a zoom (Cursorful's ≥2 rule). */
2090
2127
  minClusterClicks?: number;
2128
+ /** the level a drag is followed at (see ZoomStyleParams.dragLevel). */
2129
+ dragLevel?: number;
2091
2130
  /** emit spans with focusMode 'auto' (cursor-follow camera). */
2092
2131
  followByDefault?: boolean;
2093
2132
  /** typing sessions plan spans. */
@@ -2337,10 +2376,12 @@ declare function cameraModel(frame: {
2337
2376
  camera?: string | null;
2338
2377
  } | null | undefined): CameraModel;
2339
2378
  /**
2340
- * How much of the level the stage camera spends pulling the focus to the
2341
- * frame's centre: 0 at level 1 (the identity), fully centred from level
2342
- * 1 + CAMERA_CENTRE_RAMP. A smoothstep, so a ramp that passes through the
2343
- * band slides the card without a kink.
2379
+ * The stage camera's centring is a component of the zoom TRACK (0 at rest,
2380
+ * 1 at an apex, eased with the level), so the card's slide and its scale
2381
+ * are one motion. This band is the FALLBACK for a track lowered before the
2382
+ * component existed: centring as a smoothstep of the level over
2383
+ * 1 .. 1 + CAMERA_CENTRE_RAMP. Keyed to the level it front-loaded the
2384
+ * slide, since any ease crosses the band in its first frames (the twitch).
2344
2385
  */
2345
2386
  declare const CAMERA_CENTRE_RAMP = 0.3;
2346
2387
  /**
@@ -2372,7 +2413,14 @@ interface ZoomView {
2372
2413
  ox: number;
2373
2414
  oy: number;
2374
2415
  }
2375
- declare function zoomView(level: number, cx: number, cy: number, layout: CardLayout, camera?: CameraModel): ZoomView;
2416
+ declare function zoomView(level: number, cx: number, cy: number, layout: CardLayout, camera?: CameraModel,
2417
+ /**
2418
+ * The stage camera's centring at this instant, 0..1: the zoom track's
2419
+ * fourth component (0 at rest, 1 at an apex, eased with the level).
2420
+ * Absent = the apex (1) under the stage camera, which is what a span's
2421
+ * stored focus describes; always 0 under the magnifier.
2422
+ */
2423
+ centring?: number): ZoomView;
2376
2424
  /**
2377
2425
  * The visible window as fractions of the canvas (x/y its top-left, w/h its
2378
2426
  * size, both 1/level): what the studio's aiming rect draws and what the
@@ -2380,7 +2428,7 @@ declare function zoomView(level: number, cx: number, cy: number, layout: CardLay
2380
2428
  * under the stage camera; it never does under the magnifier once the focus
2381
2429
  * is clamped.
2382
2430
  */
2383
- declare function zoomViewport(level: number, cx: number, cy: number, layout: CardLayout, camera?: CameraModel): {
2431
+ declare function zoomViewport(level: number, cx: number, cy: number, layout: CardLayout, camera?: CameraModel, centring?: number): {
2384
2432
  x: number;
2385
2433
  y: number;
2386
2434
  w: number;
@@ -2392,7 +2440,7 @@ declare function zoomViewport(level: number, cx: number, cy: number, layout: Car
2392
2440
  * `level`. The aiming rect's corner drag re-derives its focus through this
2393
2441
  * so the rect stays under the pointer in either camera model.
2394
2442
  */
2395
- declare function focusForViewportCentre(cX: number, cY: number, level: number, layout: CardLayout, camera?: CameraModel): {
2443
+ declare function focusForViewportCentre(cX: number, cY: number, level: number, layout: CardLayout, camera?: CameraModel, centring?: number): {
2396
2444
  cx: number;
2397
2445
  cy: number;
2398
2446
  };
@@ -2835,6 +2883,13 @@ interface FollowOptions {
2835
2883
  interface FollowEvent {
2836
2884
  /** SOURCE seconds — the moment the recenter starts. */
2837
2885
  t: number;
2886
+ /**
2887
+ * A PATH sample: the camera is at this focus AT `t` (a linear segment
2888
+ * from the previous keyframe), not the start of a glide toward it. Baked
2889
+ * every FOLLOW_PATH_STEP through a drag, so the camera pans with the
2890
+ * pointer for the whole press.
2891
+ */
2892
+ path?: true;
2838
2893
  cx: number;
2839
2894
  cy: number;
2840
2895
  }
@@ -4451,4 +4506,4 @@ declare function rejectSpan(rejected: RejectedSpan[] | undefined, lane: Rejected
4451
4506
  anchor?: StepAnchor;
4452
4507
  }, note?: string): RejectedSpan;
4453
4508
 
4454
- export { ASPECT_RATIOS, AUDIO_PLAN_STEP, type AnchorKind, type Anim, type AnimKind, type AnimSide, type AnimStep, type AppliedTemplate, type ApplyTemplateOptions, type AspectRatioOption, type AudioClip, type AudioPlanPoint, type AudioPlanTrack, BACKGROUND_Z, BASE_FRAME_STYLE, type Backdrop, type BackgroundMedia, type BakedText3dAsset, type BakedText3dMaterial, type BrowserBarStyle, type BuildDigestInput, CAMERA_CENTRE_RAMP, CAMERA_COVER_MIN, CAMERA_FAR, CAMERA_NEAR, CAM_CHAIN_GAP, CAM_EASE, CAM_PAN, CAM_PAN_EASE, CAM_RAMP_IN, CAM_RAMP_OUT, CAM_SIZE_MAX, CAM_SIZE_MIN, CAM_SPAN_MIN, CAPTURE_COVERAGE_MIN, CARD_EDGE_OVERDRAW, CARD_ENTER_KINDS, CARD_EXIT_KINDS, CARD_FADE_SECONDS, CARD_FOV, CARD_Z, CHANNEL_SPECS_HASH, CHANNEL_SPECS_VERIFIED, CLICK_FX_INTENSITY, CLICK_FX_PRE, CLICK_HIGHLIGHT_FADE, CLICK_PAIR_MAX, CLICK_PULSE_DUR, CLICK_RECT_MAX_FRAC, CLICK_RIPPLE_DUR, CLICK_SYNTH_RELEASE, CROP_MAX_PX, CROP_MIN_FRAC, CROP_MIN_PX, CROP_PAD, CURSOR_IDLE_EPS_FRAC, CURSOR_IDLE_FADE_IN, CURSOR_IDLE_FADE_OUT, CURSOR_IDLE_HOLD, type CamBubbleRect, type CamPoseSpan, type CamStyle, type CameraModel, type CaptureNormalization, type CardLayout, type ClickFxStyle, type CopiedLayout, type CopyLayoutOptions, type CopyStyleOptions, type CursorEvent, type CursorFadeKey, type CursorIdleOptions, type CursorStyle, type CursorTrack, DARK_GROUND, DEFAULT_BROWSER_BAR, DEFAULT_CAM_POSE, DEFAULT_CAM_STYLE, DEFAULT_CLICK_FX, DEFAULT_CURSOR_STYLE, DEFAULT_DUCK, DEFAULT_FRAME_STYLE, DEFAULT_SPEED_PARAMS, DEFAULT_TILT_POSE, DEFAULT_ZOOM_LEVEL, DEFAULT_ZOOM_STYLE, DESIGN_FRAME_WIDTH, DESTINATIONS, DIGEST_CROP_MAX, DIGEST_FULL_MAX, DIGEST_VERSION, DOC_SCHEMA_VERSION, DRAG_FIT_LEVEL, type Destination, type Digest, type DigestImageRef, type DigestPlan, type DigestTakeFacts, type DocSegment, type DuckOptions, END_CARD_FROM, END_CARD_RECEDE, END_CARD_SECONDS, ENTRANCE_SECONDS, EXPORT_RESOLUTION_OPTIONS, EXPORT_SHORT_EDGE, type EndCard, type EnvelopePoint, type ExportResolution, type ExtractClickOptions, FOLLOW_RECENTER, FOLLOW_SAFE_RATIO, FRAME_BORDER_COLOR_DEFAULT, FRAME_BORDER_DEFAULT, FRAME_BORDER_WIDTH_DEFAULT, FREEZE_DEFAULT_SECONDS, FREEZE_SECONDS_MAX, FREEZE_SECONDS_MIN, type FocusBounds, type FollowEvent, type FollowOptions, type FrameEntrance, type FrameGeometry, type FrameStyle, type FreezeExtent, type FreezeSpan, HOUSE_GRADIENT, HTML_LAYER_FOREIGN_FIELDS, HTML_LAYER_MAX_BYTES, HTML_LAYER_SVG_CODE, HTML_LAYER_WARN_BYTES, HTML_STARTERS, type HtmlLayerFace, type HtmlLayerInlineFace, type HtmlLayerPayload, type HtmlLayerProblem, type HtmlLayerProblemCode, type HtmlLayerSource, type HtmlOverlayClip, type HtmlOverlayFont, type HtmlStarter, IDLE_KINDS, type IdleKind, type IngestOptions, LAYER_CARD_SHADOW, LAYOUT_FRAME_FIELDS, LOOK_KINDS, type LayerCard, type LayoutParts, type Look, type LookBrand, type LookKind, type LookPlacement, type LowerProgramOptions, type LoweredAudioClip, type LoweredClick, type LoweredComposition, type LoweredZoomSpan, MEDIA_ENTER_KINDS, MEDIA_EXIT_KINDS, MEDIA_FRAME_KEYS, MEDIA_KEY_PREFIX, MINIMAL_BAR_THEMES, MOTION_DELTA, MOTION_EASE, type Media, type MediaFrame, type MediaFrameKey, type MediaOverlayClip, type MediaSegment, type MicRms, type MinimalBarTheme, type Moment, type MomentKind, type MomentsOptions, type MotionKey, type MotionPose, type MotionPose3D, type MusicBedInput, type NormRect, OBJECT_DEFAULT_SCALE, OVERLAY_FONT_FACES, OVERLAY_LINE_HEIGHT, OVERLAY_MEDIA_DEFAULT_RADIUS, OVERLAY_MEDIA_DEFAULT_WIDTH, OVERLAY_MIN_DURATION, OVERLAY_SCALE_MAX, OVERLAY_SIZE_MAX, OVERLAY_SIZE_MIN, OVERLAY_TRANSITION_DUR, OVERLAY_Z, type ObjectAnimation, type ObjectAsset, type ObjectClip, type ObjectPrimitiveShape, type OverlayClip, type OverlayFontFace, type OverlayKind, type OverlayPresetStyle, type OverlayRect, type OverlayTransform, type OverlayTransition, PLATE_GROUND, PLAYBACK_ACTIVITY, PRIMARY_MEDIA, PROGRAM_RETIME, type PlanOptions, type PlanTiltOptions, type PlaneSize, type ProgramAnchorDoc, type ProgramTweenEdit, type ProjectDoc, type PxRect, REJECT_OVERLAP, REST_TILT_ID, type RecordingArtifact, type RecordingMeta, type Rect, type RejectedLane, type RejectedSpan, type ResolvedOverlayBox, type ResolvedOverlayStyle, SCENE_MOTION, SCENE_QUIET, SEGMENT_ENTER_KINDS, SEGMENT_EXIT_KINDS, SPEED_RATE_MAX, SPEED_RATE_MIN, SPEED_SPAN_MIN, STAGE_CLIP_PREFIX, STILL_DEFAULT, STUDIO_ENTRY_ID, STYLE_FIELDS, type SegmentExtent, type SmoothOptions, type SmoothPoint, type SpeedParams, type SpeedSpan, type StepAnchor, type StepSpan, type StudioAudioPlan, type StudioDoc, type StudioEntry, type StyleField, TEXT3D_DEPTH_DEFAULT, TEXT3D_DEPTH_MAX, TEXT3D_DEPTH_MIN, TEXT_ENTER_KINDS, TEXT_EXIT_KINDS, TEXT_PRESETS, TILT_AUTO_DEAD_ZONE, TILT_AUTO_MIN, TILT_CHAIN_GAP, TILT_DEG_MAX, TILT_EASE, TILT_INTENSITY_MAX, TILT_PAN, TILT_PAN_EASE, TILT_RAMP_IN, TILT_RAMP_OUT, TILT_SPAN_MIN, TILT_UI_DEG_MAX, TRANSITION_SECONDS, TRANSITION_SECONDS_MAX, TRANSITION_SECONDS_MIN, TRANSITION_SPEED_MULT, type TemplateAnchor, type Text3dMaterial, type TextFxDirection, type TextFxKind, type TextFxSpec, type TextFxUnit, type TextOverlayBox, type TextOverlayClip, type TextOverlayPreset, type TextOverlayStroke, type TiltPersonality, type TiltSpan, type TiltStyleName, type TranscriptSegment, type Transition, type TransitionKind, type TransitionMove, type TransitionOut, type TransitionSpeed, WINDOW_FOCUS_MIN, ZOOM_CHAIN_GAP, ZOOM_EASE, ZOOM_LEVELS, ZOOM_LEVEL_MAX, ZOOM_LEVEL_MIN, ZOOM_PAN, ZOOM_PAN_EASE, ZOOM_RAMP_IN, ZOOM_RAMP_IN_OVERLAP, ZOOM_RAMP_OUT, ZOOM_SPAN_MIN, ZOOM_STYLES, ZOOM_STYLE_OPTIONS, type ZoomSpan, type ZoomStyleName, type ZoomStyleParams, type ZoomView, type ZoomWindow, anchorKindOf, anchorSourceDuration, animStep, applyLook, applyTemplate, aspectRatioValue, audioLane, backdropMedia, buildDigest, camBubbleRect, camBubbleRectAt, camLane, camMoveLane, camRectFromPose, camRestPose, camTrackFromDoc, cameraCentring, cameraModel, cardEnter, cardExit, cardFields, cardInset, cardPointToScreen, cardPoseTrack, clampCamFrac, clampCamSize, clampFocus, clampFreezeSeconds, clampSpeedRate, clampTiltDeg, clampZoomLevel, clipEnvelope, clipLength, clipsFrom, computeCardLayout, computeMicRms, computePeaks, copyLayout, copyStyle, cropBox, cssFamilies, cssWeights, cursorIdleFade, deriveViewportCrop, destinationById, destinationsForChannel, docCardLayout, docFreezes, docMediaList, docOutputDuration, docRestTime, docStillTime, docToCropSpace, docToFullSpace, docTransitions, dropTemplate, duckCurve, effectiveSegments, endCardClips, enterKey, enterOf, entranceSeconds, envelopeAt, envelopeValueAt, exitKey, exitOf, exitSeconds, expandEndCard, expectedFrameSize, exportSizeFor, extractClicks, focusBounds, focusForViewportCentre, followFocusEvents, frameGeometry, freezeLane, freezeOutputExtents, hexToRgbTriplet, houseLook, htmlLayerBleed, htmlLayerBleedFor, htmlLayerFaces, htmlLayerKey, htmlLayerLabel, htmlLayerPayload, htmlLayerPictureBox, htmlLayerProblems, htmlLayerSvg, htmlLayerText, htmlLayerUnhostedFamilies, htmlLayerWidth, htmlStarterClip, idleGaps, idleKey, isKeyedOverlay, isLookKind, isMusicBed, isPlayback, isProgramDoc, isRecordingDoc, isRejected, isStageClip, isUnitStep, lastLayerEnd, layerMedia, layoutOf, levelForFocusFraction, lookFromBrand, lookKindForGround, lowerProgramDoc, lowerStudioDoc, lowerToComposition, mediaAtOutput, mediaDuration, mediaFrame, mediaKey, mediaRef, mediaRefId, mediaSource, micLane, migrateHostedDoc, migrateMotion, momentsFromDoc, motionTrack, musicBedClip, nextMediaId, normalizeCaptureSpace, objectMotionBase, objectMotionPoseAt, objectsLane, outputDurationOf, outputEnd, outputRangeToSource, overlapFraction, overlayFaceFor, overlayFontFaces, overlayFontString, overlayFxSpec, overlayHit, overlayLines, overlayMotionBase, overlayMotionPoseAt, overlayRect, overlaysLane, ownFreezes, pageDisplayUrl, parsePoseId, pickStyle, placeFreezes, planAutoSpeed, planAutoTilt, planAutoZoom, planForDigest, planeSizeAtDepth, platformBarKind, programDuration, projectFromArtifact, ratedSegments, recommendedExportResolution, refillAudioBeds, rejectSpan, removeSourceRange, removeSpeedInRange, resolveExportSize, resolveOverlayBox, resolveOverlayStyle, resolveText3dAsset, resolveZoomStyle, restLeanOf, restSpansThroughTransitions, sameMedia, sceneChanges, scrollRuns, segmentOutputExtents, outputLen as segmentOutputLen, segmentStarts, setSpeedInRange, slideSide, smoothCursor, spanOutputExtent, speedLane, studioAudioPlan, studioEntry, studioLayerData, tiltLane, tiltTrackFromDoc, transitionMult, transitionSeconds, videoLane, voiceKey, wellFormedXml, withBackdrop, withFreezes, withHolds, withoutRejected, wrapProgramLength, zoomCoversRect, zoomLane, zoomSpanForRange, zoomTrackFromDoc, zoomView, zoomViewport, zoomWindow };
4509
+ export { ASPECT_RATIOS, AUDIO_PLAN_STEP, type AnchorKind, type Anim, type AnimKind, type AnimSide, type AnimStep, type AppliedTemplate, type ApplyTemplateOptions, type AspectRatioOption, type AudioClip, type AudioPlanPoint, type AudioPlanTrack, BACKGROUND_Z, BASE_FRAME_STYLE, type Backdrop, type BackgroundMedia, type BakedText3dAsset, type BakedText3dMaterial, type BrowserBarStyle, type BuildDigestInput, CAMERA_CENTRE_RAMP, CAMERA_COVER_MIN, CAMERA_FAR, CAMERA_NEAR, CAM_CHAIN_GAP, CAM_EASE, CAM_PAN, CAM_PAN_EASE, CAM_RAMP_IN, CAM_RAMP_OUT, CAM_SIZE_MAX, CAM_SIZE_MIN, CAM_SPAN_MIN, CAPTURE_COVERAGE_MIN, CARD_EDGE_OVERDRAW, CARD_ENTER_KINDS, CARD_EXIT_KINDS, CARD_FADE_SECONDS, CARD_FOV, CARD_Z, CHANNEL_SPECS_HASH, CHANNEL_SPECS_VERIFIED, CLICK_FX_INTENSITY, CLICK_FX_PRE, CLICK_HIGHLIGHT_FADE, CLICK_PAIR_MAX, CLICK_PULSE_DUR, CLICK_RECT_MAX_FRAC, CLICK_RIPPLE_DUR, CLICK_SYNTH_RELEASE, CROP_MAX_PX, CROP_MIN_FRAC, CROP_MIN_PX, CROP_PAD, CURSOR_IDLE_EPS_FRAC, CURSOR_IDLE_FADE_IN, CURSOR_IDLE_FADE_OUT, CURSOR_IDLE_HOLD, type CamBubbleRect, type CamPoseSpan, type CamStyle, type CameraModel, type CaptureNormalization, type CardLayout, type ClickFxStyle, type CopiedLayout, type CopyLayoutOptions, type CopyStyleOptions, type CursorEvent, type CursorFadeKey, type CursorIdleOptions, type CursorStyle, type CursorTrack, DARK_GROUND, DEFAULT_BROWSER_BAR, DEFAULT_CAM_POSE, DEFAULT_CAM_STYLE, DEFAULT_CLICK_FX, DEFAULT_CURSOR_STYLE, DEFAULT_DUCK, DEFAULT_FRAME_STYLE, DEFAULT_SPEED_PARAMS, DEFAULT_TILT_POSE, DEFAULT_ZOOM_LEVEL, DEFAULT_ZOOM_STYLE, DESIGN_FRAME_WIDTH, DESTINATIONS, DIGEST_CROP_MAX, DIGEST_FULL_MAX, DIGEST_VERSION, DOC_SCHEMA_VERSION, DRAG_FIT_LEVEL, DRAG_MIN_S, DRAG_MIN_TRAVEL_FRAC, type Destination, type Digest, type DigestImageRef, type DigestPlan, type DigestTakeFacts, type DocSegment, type Drag, type DuckOptions, END_CARD_FROM, END_CARD_RECEDE, END_CARD_SECONDS, ENTRANCE_SECONDS, EXPORT_RESOLUTION_OPTIONS, EXPORT_SHORT_EDGE, type EndCard, type EnvelopePoint, type ExportResolution, type ExtractClickOptions, FOLLOW_RECENTER, FOLLOW_SAFE_RATIO, FRAME_BORDER_COLOR_DEFAULT, FRAME_BORDER_DEFAULT, FRAME_BORDER_WIDTH_DEFAULT, FREEZE_DEFAULT_SECONDS, FREEZE_SECONDS_MAX, FREEZE_SECONDS_MIN, type FocusBounds, type FollowEvent, type FollowOptions, type FrameEntrance, type FrameGeometry, type FrameStyle, type FreezeExtent, type FreezeSpan, HOUSE_GRADIENT, HTML_LAYER_FOREIGN_FIELDS, HTML_LAYER_MAX_BYTES, HTML_LAYER_SVG_CODE, HTML_LAYER_WARN_BYTES, HTML_STARTERS, type HtmlLayerFace, type HtmlLayerInlineFace, type HtmlLayerPayload, type HtmlLayerProblem, type HtmlLayerProblemCode, type HtmlLayerSource, type HtmlOverlayClip, type HtmlOverlayFont, type HtmlStarter, IDLE_KINDS, type IdleKind, type IngestOptions, LAYER_CARD_SHADOW, LAYOUT_FRAME_FIELDS, LOOK_KINDS, type LayerCard, type LayoutParts, type Look, type LookBrand, type LookKind, type LookPlacement, type LowerProgramOptions, type LoweredAudioClip, type LoweredClick, type LoweredComposition, type LoweredZoomSpan, MEDIA_ENTER_KINDS, MEDIA_EXIT_KINDS, MEDIA_FRAME_KEYS, MEDIA_KEY_PREFIX, MINIMAL_BAR_THEMES, MOTION_DELTA, MOTION_EASE, type Media, type MediaFrame, type MediaFrameKey, type MediaOverlayClip, type MediaSegment, type MicRms, type MinimalBarTheme, type Moment, type MomentKind, type MomentsOptions, type MotionKey, type MotionPose, type MotionPose3D, type MusicBedInput, type NormRect, OBJECT_DEFAULT_SCALE, OVERLAY_FONT_FACES, OVERLAY_LINE_HEIGHT, OVERLAY_MEDIA_DEFAULT_RADIUS, OVERLAY_MEDIA_DEFAULT_WIDTH, OVERLAY_MIN_DURATION, OVERLAY_SCALE_MAX, OVERLAY_SIZE_MAX, OVERLAY_SIZE_MIN, OVERLAY_TRANSITION_DUR, OVERLAY_Z, type ObjectAnimation, type ObjectAsset, type ObjectClip, type ObjectPrimitiveShape, type OverlayClip, type OverlayFontFace, type OverlayKind, type OverlayPresetStyle, type OverlayRect, type OverlayTransform, type OverlayTransition, PLATE_GROUND, PLAYBACK_ACTIVITY, PRIMARY_MEDIA, PROGRAM_RETIME, type PlanOptions, type PlanTiltOptions, type PlaneSize, type ProgramAnchorDoc, type ProgramTweenEdit, type ProjectDoc, type PxRect, REJECT_OVERLAP, REST_TILT_ID, type RecordingArtifact, type RecordingMeta, type Rect, type RejectedLane, type RejectedSpan, type ResolvedOverlayBox, type ResolvedOverlayStyle, SCENE_MOTION, SCENE_QUIET, SEGMENT_ENTER_KINDS, SEGMENT_EXIT_KINDS, SPEED_RATE_MAX, SPEED_RATE_MIN, SPEED_SPAN_MIN, STAGE_CLIP_PREFIX, STILL_DEFAULT, STUDIO_ENTRY_ID, STYLE_FIELDS, type SegmentExtent, type SmoothOptions, type SmoothPoint, type SpeedParams, type SpeedSpan, type StepAnchor, type StepSpan, type StudioAudioPlan, type StudioDoc, type StudioEntry, type StyleField, TEXT3D_DEPTH_DEFAULT, TEXT3D_DEPTH_MAX, TEXT3D_DEPTH_MIN, TEXT_ENTER_KINDS, TEXT_EXIT_KINDS, TEXT_PRESETS, TILT_AUTO_DEAD_ZONE, TILT_AUTO_MIN, TILT_CHAIN_GAP, TILT_DEG_MAX, TILT_EASE, TILT_INTENSITY_MAX, TILT_PAN, TILT_PAN_EASE, TILT_RAMP_IN, TILT_RAMP_OUT, TILT_SPAN_MIN, TILT_UI_DEG_MAX, TRANSITION_SECONDS, TRANSITION_SECONDS_MAX, TRANSITION_SECONDS_MIN, TRANSITION_SPEED_MULT, type TemplateAnchor, type Text3dMaterial, type TextFxDirection, type TextFxKind, type TextFxSpec, type TextFxUnit, type TextOverlayBox, type TextOverlayClip, type TextOverlayPreset, type TextOverlayStroke, type TiltPersonality, type TiltSpan, type TiltStyleName, type TranscriptSegment, type Transition, type TransitionKind, type TransitionMove, type TransitionOut, type TransitionSpeed, WINDOW_FOCUS_MIN, ZOOM_CHAIN_GAP, ZOOM_EASE, ZOOM_LEVELS, ZOOM_LEVEL_MAX, ZOOM_LEVEL_MIN, ZOOM_PAN, ZOOM_PAN_EASE, ZOOM_RAMP_IN, ZOOM_RAMP_IN_OVERLAP, ZOOM_RAMP_OUT, ZOOM_SPAN_MIN, ZOOM_STYLES, ZOOM_STYLE_OPTIONS, type ZoomSpan, type ZoomStyleName, type ZoomStyleParams, type ZoomView, type ZoomWindow, anchorKindOf, anchorSourceDuration, animStep, applyLook, applyTemplate, aspectRatioValue, audioLane, backdropMedia, buildDigest, camBubbleRect, camBubbleRectAt, camLane, camMoveLane, camRectFromPose, camRestPose, camTrackFromDoc, cameraCentring, cameraModel, cardEnter, cardExit, cardFields, cardInset, cardPointToScreen, cardPoseTrack, clampCamFrac, clampCamSize, clampFocus, clampFreezeSeconds, clampSpeedRate, clampTiltDeg, clampZoomLevel, clipEnvelope, clipLength, clipsFrom, computeCardLayout, computeMicRms, computePeaks, copyLayout, copyStyle, cropBox, cssFamilies, cssWeights, cursorIdleFade, deriveViewportCrop, destinationById, destinationsForChannel, docCardLayout, docFreezes, docMediaList, docOutputDuration, docRestTime, docStillTime, docToCropSpace, docToFullSpace, docTransitions, dragsFromTrack, dropTemplate, duckCurve, effectiveSegments, endCardClips, enterKey, enterOf, entranceSeconds, envelopeAt, envelopeValueAt, exitKey, exitOf, exitSeconds, expandEndCard, expectedFrameSize, exportSizeFor, extractClicks, focusBounds, focusForViewportCentre, followFocusEvents, frameGeometry, freezeLane, freezeOutputExtents, hexToRgbTriplet, houseLook, htmlLayerBleed, htmlLayerBleedFor, htmlLayerFaces, htmlLayerKey, htmlLayerLabel, htmlLayerPayload, htmlLayerPictureBox, htmlLayerProblems, htmlLayerSvg, htmlLayerText, htmlLayerUnhostedFamilies, htmlLayerWidth, htmlStarterClip, idleGaps, idleKey, isKeyedOverlay, isLookKind, isMusicBed, isPlayback, isProgramDoc, isRecordingDoc, isRejected, isStageClip, isUnitStep, lastLayerEnd, layerMedia, layoutOf, levelForFocusFraction, lookFromBrand, lookKindForGround, lowerProgramDoc, lowerStudioDoc, lowerToComposition, mediaAtOutput, mediaDuration, mediaFrame, mediaKey, mediaRef, mediaRefId, mediaSource, micLane, migrateHostedDoc, migrateMotion, momentsFromDoc, motionTrack, musicBedClip, nextMediaId, normalizeCaptureSpace, objectMotionBase, objectMotionPoseAt, objectsLane, outputDurationOf, outputEnd, outputRangeToSource, overlapFraction, overlayFaceFor, overlayFontFaces, overlayFontString, overlayFxSpec, overlayHit, overlayLines, overlayMotionBase, overlayMotionPoseAt, overlayRect, overlaysLane, ownFreezes, pageDisplayUrl, parsePoseId, pickStyle, placeFreezes, planAutoSpeed, planAutoTilt, planAutoZoom, planForDigest, planeSizeAtDepth, platformBarKind, programDuration, projectFromArtifact, ratedSegments, recommendedExportResolution, refillAudioBeds, rejectSpan, removeSourceRange, removeSpeedInRange, resolveExportSize, resolveOverlayBox, resolveOverlayStyle, resolveText3dAsset, resolveZoomStyle, restLeanOf, restSpansThroughTransitions, sameMedia, sceneChanges, scrollRuns, segmentOutputExtents, outputLen as segmentOutputLen, segmentStarts, setSpeedInRange, slideSide, smoothCursor, spanOutputExtent, speedLane, studioAudioPlan, studioEntry, studioLayerData, tiltLane, tiltTrackFromDoc, transitionMult, transitionSeconds, videoLane, voiceKey, wellFormedXml, withBackdrop, withFreezes, withHolds, withoutRejected, wrapProgramLength, zoomCoversRect, zoomLane, zoomSpanForRange, zoomTrackFromDoc, zoomView, zoomViewport, zoomWindow };
package/dist/index.js CHANGED
@@ -1156,6 +1156,7 @@ var ZOOM_STYLES = {
1156
1156
  targetFill: 0.42,
1157
1157
  minLevel: 1.3,
1158
1158
  maxLevel: 1.8,
1159
+ dragLevel: 1.5,
1159
1160
  lead: 0.35,
1160
1161
  hold: 1.2,
1161
1162
  followByDefault: true,
@@ -1168,8 +1169,13 @@ var ZOOM_STYLES = {
1168
1169
  rampOut: 0.5,
1169
1170
  chainGap: 3,
1170
1171
  pan: 0.55,
1171
- ease: "css-bezier(0.16, 1, 0.3, 1)",
1172
- panEase: "css-bezier(0.22, 1, 0.36, 1)",
1172
+ // The reference's corner trace at 60 fps: a quarter of the move by a
1173
+ // fifth of the time, two thirds by half, the rest a settle. That is
1174
+ // CSS `ease` (0.25, 0.1, 0.25, 1), a small lead-in and a long tail;
1175
+ // (0.16, 1, 0.3, 1) put half the move in the first sixty milliseconds
1176
+ // and read as a twitch.
1177
+ ease: "css-bezier(0.25, 0.1, 0.25, 1)",
1178
+ panEase: "css-bezier(0.3, 0.05, 0.25, 1)",
1173
1179
  followSafeRatio: 0.45,
1174
1180
  followRecenter: 0.55,
1175
1181
  followLookahead: 0.4,
@@ -1183,6 +1189,7 @@ var ZOOM_STYLES = {
1183
1189
  targetFill: 0.5,
1184
1190
  minLevel: 1.5,
1185
1191
  maxLevel: 2.2,
1192
+ dragLevel: 1.7,
1186
1193
  lead: 0.35,
1187
1194
  hold: 1,
1188
1195
  followByDefault: false,
@@ -1210,6 +1217,7 @@ var ZOOM_STYLES = {
1210
1217
  targetFill: 0.45,
1211
1218
  minLevel: 1.35,
1212
1219
  maxLevel: 2,
1220
+ dragLevel: 1.5,
1213
1221
  lead: 0.6,
1214
1222
  hold: 1.4,
1215
1223
  followByDefault: true,
@@ -1237,6 +1245,7 @@ var ZOOM_STYLES = {
1237
1245
  targetFill: 0.5,
1238
1246
  minLevel: 1.4,
1239
1247
  maxLevel: 2.5,
1248
+ dragLevel: 1.7,
1240
1249
  lead: 0.25,
1241
1250
  hold: 1,
1242
1251
  followByDefault: false,
@@ -1264,6 +1273,7 @@ var ZOOM_STYLES = {
1264
1273
  targetFill: 0.5,
1265
1274
  minLevel: 1.5,
1266
1275
  maxLevel: 2.2,
1276
+ dragLevel: 1.7,
1267
1277
  lead: 0.2,
1268
1278
  hold: 1,
1269
1279
  followByDefault: false,
@@ -1292,6 +1302,7 @@ var ZOOM_STYLES = {
1292
1302
  targetFill: 0.42,
1293
1303
  minLevel: 1.3,
1294
1304
  maxLevel: 1.8,
1305
+ dragLevel: 1.5,
1295
1306
  lead: 0.5,
1296
1307
  hold: 0.6,
1297
1308
  followByDefault: false,
@@ -1329,6 +1340,7 @@ var ZOOM_STYLES = {
1329
1340
  targetFill: 0.42,
1330
1341
  minLevel: 1.3,
1331
1342
  maxLevel: 1.8,
1343
+ dragLevel: 1.5,
1332
1344
  lead: 0.5,
1333
1345
  hold: 0.7,
1334
1346
  followByDefault: true,
@@ -1366,6 +1378,7 @@ var ZOOM_STYLES = {
1366
1378
  targetFill: 0.45,
1367
1379
  minLevel: 1.35,
1368
1380
  maxLevel: 2,
1381
+ dragLevel: 1.5,
1369
1382
  lead: 0.6,
1370
1383
  hold: 1.4,
1371
1384
  followByDefault: true,
@@ -1436,6 +1449,37 @@ var DWELL_SPACING = 1.8;
1436
1449
  var DRAG_FIT_LEVEL = 1.15;
1437
1450
  var PRESS_ECHO_MS = 4;
1438
1451
  var PRESS_ECHO_PX = 2;
1452
+ var DRAG_MIN_TRAVEL_FRAC = 0.015;
1453
+ var DRAG_MIN_S = 0.2;
1454
+ function dragsFromTrack(track, width, height) {
1455
+ if (!(width > 0) || !(height > 0)) return [];
1456
+ const downs = dedupePresses(track.filter((e) => e.type === "down"));
1457
+ const out = [];
1458
+ for (const d of downs) {
1459
+ const up = track.find((e) => e.type === "up" && e.t > d.t);
1460
+ if (!up) continue;
1461
+ const t0 = d.t / 1e3;
1462
+ const t1 = up.t / 1e3;
1463
+ if (t1 - t0 < DRAG_MIN_S) continue;
1464
+ let travel = 0;
1465
+ for (const e of track) {
1466
+ if (e.t < d.t) continue;
1467
+ if (e.t > up.t) break;
1468
+ if (e.type !== "move" && e.type !== "up") continue;
1469
+ const dist = Math.hypot(e.x - d.x, e.y - d.y) / width;
1470
+ if (dist > travel) travel = dist;
1471
+ }
1472
+ if (travel < DRAG_MIN_TRAVEL_FRAC) continue;
1473
+ out.push({
1474
+ t0,
1475
+ t1,
1476
+ nx: clamp012(d.x / width),
1477
+ ny: clamp012(d.y / height),
1478
+ travel
1479
+ });
1480
+ }
1481
+ return out;
1482
+ }
1439
1483
  var TYPING_MIN_PINGS = 2;
1440
1484
  var TYPING_MIN_DUR = 0.5;
1441
1485
  var TYPING_CLICK_ABSORB = 1.5;
@@ -1513,13 +1557,14 @@ function planAutoZoom(track, options) {
1513
1557
  lead = style.lead,
1514
1558
  hold = style.hold,
1515
1559
  minClusterClicks = style.minClusterClicks,
1560
+ dragLevel = style.dragLevel,
1516
1561
  followByDefault = style.followByDefault,
1517
1562
  typingZoom = style.typingZoom,
1518
1563
  typingGap = style.typingGap,
1519
1564
  typingHold = style.typingHold,
1520
1565
  typingMinLevel = style.typingMinLevel
1521
1566
  } = options;
1522
- const { sessions, clusters, surfaces } = groupTrack(track, {
1567
+ const grouped = groupTrack(track, {
1523
1568
  width,
1524
1569
  height,
1525
1570
  clusterGap,
@@ -1527,6 +1572,22 @@ function planAutoZoom(track, options) {
1527
1572
  typingZoom,
1528
1573
  targetFill
1529
1574
  });
1575
+ const { sessions } = grouped;
1576
+ const drags = dragsFromTrack(track, width, height);
1577
+ const dragPress = new Set(drags.map((d) => round(d.t0)));
1578
+ const withoutDrags = (list) => list.map((cl) => cl.filter((c) => !dragPress.has(round(c.t)))).filter((cl) => cl.length > 0);
1579
+ const clusters = withoutDrags(grouped.clusters);
1580
+ const surfaces = withoutDrags(grouped.surfaces);
1581
+ const dragSpans = drags.map((d, i) => ({
1582
+ id: `g${i}`,
1583
+ in: round(Math.max(0, d.t0 - lead)),
1584
+ out: round(d.t1 + hold),
1585
+ level: clampZoomLevel(Math.min(Math.max(dragLevel, 1.1), maxLevel)),
1586
+ cx: round(d.nx),
1587
+ cy: round(d.ny),
1588
+ focusMode: "auto",
1589
+ source: "auto"
1590
+ }));
1530
1591
  const eligible = clusters.filter((c) => c.length >= minClusterClicks);
1531
1592
  const clickSpans = [];
1532
1593
  const dragReserved = surfaces.map((cluster, i) => {
@@ -1615,7 +1676,7 @@ function planAutoZoom(track, options) {
1615
1676
  // the FIELD is the anchor, and a follow would be a no-op at best.
1616
1677
  source: "auto"
1617
1678
  }));
1618
- const spans = [...zSpans, ...kSpans].sort((a, b) => a.in - b.in);
1679
+ const spans = [...zSpans, ...kSpans, ...dragSpans].sort((a, b) => a.in - b.in);
1619
1680
  const dwells = dwellSpans(track, width, height, maxLevel, [
1620
1681
  ...spans,
1621
1682
  ...dragReserved
@@ -1964,19 +2025,19 @@ function cameraCentring(level) {
1964
2025
  const u = clamp013((level - 1) / CAMERA_CENTRE_RAMP);
1965
2026
  return u * u * (3 - 2 * u);
1966
2027
  }
1967
- function zoomView(level, cx, cy, layout, camera = "card") {
2028
+ function zoomView(level, cx, cy, layout, camera = "card", centring) {
1968
2029
  const L = Math.max(1, level);
1969
2030
  const fx = layout.dx + cx * layout.dw;
1970
2031
  const fy = layout.dy + cy * layout.dh;
1971
2032
  const ox = layout.W / 2;
1972
2033
  const oy = layout.H / 2;
1973
- const t = camera === "stage" ? cameraCentring(L) : 0;
2034
+ const t = camera === "stage" ? clamp013(centring ?? 1) : 0;
1974
2035
  const wcx = fx + (ox - fx) / L * (1 - t);
1975
2036
  const wcy = fy + (oy - fy) / L * (1 - t);
1976
2037
  return { level: L, fx, fy, wcx, wcy, ox, oy };
1977
2038
  }
1978
- function zoomViewport(level, cx, cy, layout, camera = "card") {
1979
- const v = zoomView(level, cx, cy, layout, camera);
2039
+ function zoomViewport(level, cx, cy, layout, camera = "card", centring) {
2040
+ const v = zoomView(level, cx, cy, layout, camera, centring);
1980
2041
  const size = 1 / v.level;
1981
2042
  return {
1982
2043
  x: (v.wcx - layout.W / (2 * v.level)) / layout.W,
@@ -1985,9 +2046,9 @@ function zoomViewport(level, cx, cy, layout, camera = "card") {
1985
2046
  h: size
1986
2047
  };
1987
2048
  }
1988
- function focusForViewportCentre(cX, cY, level, layout, camera = "card") {
2049
+ function focusForViewportCentre(cX, cY, level, layout, camera = "card", centring) {
1989
2050
  const L = Math.max(1, level);
1990
- const t = camera === "stage" ? cameraCentring(L) : 0;
2051
+ const t = camera === "stage" ? clamp013(centring ?? 1) : 0;
1991
2052
  const a = 1 - (1 - t) / L;
1992
2053
  if (a <= 1e-6) return { cx: 0.5, cy: 0.5 };
1993
2054
  const fx = (cX * layout.W - layout.W / 2 * (1 - t) / L) / a;
@@ -2894,6 +2955,8 @@ function envelopeValueAt(env, t) {
2894
2955
  // src/lower/cursorFollow.ts
2895
2956
  var FOLLOW_SAFE_RATIO = ZOOM_STYLES.glide.followSafeRatio;
2896
2957
  var FOLLOW_RECENTER = ZOOM_STYLES.glide.followRecenter;
2958
+ var FOLLOW_PATH_STEP = 0.1;
2959
+ var FOLLOW_PATH_SMOOTH = 0.08;
2897
2960
  function followFocusEvents(span, cursor, space, layout, options = {}) {
2898
2961
  const safeRatio = options.safeRatio ?? FOLLOW_SAFE_RATIO;
2899
2962
  const recenter = options.recenter ?? FOLLOW_RECENTER;
@@ -2916,9 +2979,48 @@ function followFocusEvents(span, cursor, space, layout, options = {}) {
2916
2979
  let cx = entry.cx;
2917
2980
  let cy = entry.cy;
2918
2981
  let nextAllowed = span.in + recenter;
2982
+ const drags = dragsFromTrack(cursor, space.w, space.h).filter(
2983
+ (d) => d.t1 > span.in && d.t0 < span.out
2984
+ );
2985
+ const pathEvents = [];
2986
+ for (const d of drags) {
2987
+ const from = Math.max(d.t0, span.in);
2988
+ const to = Math.min(d.t1, span.out);
2989
+ for (let t = from; ; t += FOLLOW_PATH_STEP) {
2990
+ const at = Math.min(t, to);
2991
+ const a = sampleAt2(pts, at - FOLLOW_PATH_SMOOTH);
2992
+ const b = sampleAt2(pts, at);
2993
+ const c = sampleAt2(pts, at + FOLLOW_PATH_SMOOTH);
2994
+ const f = clampFocus(
2995
+ (a.nx + b.nx + c.nx) / 3,
2996
+ (a.ny + b.ny + c.ny) / 3,
2997
+ level,
2998
+ layout,
2999
+ camera
3000
+ );
3001
+ pathEvents.push({
3002
+ t: round2(at),
3003
+ path: true,
3004
+ cx: round2(f.cx),
3005
+ cy: round2(f.cy)
3006
+ });
3007
+ if (at >= to) break;
3008
+ }
3009
+ }
3010
+ let dragIdx = 0;
2919
3011
  for (const p of pts) {
2920
3012
  if (p.t < span.in) continue;
2921
3013
  if (p.t > span.out) break;
3014
+ while (dragIdx < drags.length && p.t > drags[dragIdx].t1) {
3015
+ const last = pathEvents.filter((e) => e.t <= drags[dragIdx].t1).at(-1);
3016
+ if (last) {
3017
+ cx = last.cx;
3018
+ cy = last.cy;
3019
+ }
3020
+ nextAllowed = drags[dragIdx].t1 + recenter;
3021
+ dragIdx++;
3022
+ }
3023
+ if (dragIdx < drags.length && p.t >= drags[dragIdx].t0) continue;
2922
3024
  if (p.t < nextAllowed) continue;
2923
3025
  if (Math.abs(p.nx - cx) > thrX || Math.abs(p.ny - cy) > thrY) {
2924
3026
  const target = lookahead > 0 ? sampleAt2(pts, Math.min(p.t + lookahead, span.out)) : p;
@@ -2930,7 +3032,8 @@ function followFocusEvents(span, cursor, space, layout, options = {}) {
2930
3032
  nextAllowed = p.t + recenter;
2931
3033
  }
2932
3034
  }
2933
- return { entry, events };
3035
+ const merged = [...events, ...pathEvents].sort((a, b) => a.t - b.t);
3036
+ return { entry, events: merged };
2934
3037
  }
2935
3038
  function sampleAt2(pts, t) {
2936
3039
  if (t <= pts[0].t) return pts[0];
@@ -4655,30 +4758,42 @@ function zoomTrackFromDoc(zoom, segments, style = ZOOM_STYLES[DEFAULT_ZOOM_STYLE
4655
4758
  let chained = false;
4656
4759
  for (let i = 0; i < mapped.length; i++) {
4657
4760
  const { z, tIn, tOut } = mapped[i];
4658
- const entry = [clampZoomLevel(z.level), z.cx, z.cy];
4761
+ const entry = [clampZoomLevel(z.level), z.cx, z.cy, 1];
4659
4762
  let cur = entry;
4660
4763
  const m = transitionMult(z.transition);
4661
4764
  if (!chained) {
4662
4765
  const start = push2(
4663
4766
  tIn - (style.rampIn - style.rampInOverlap) * m,
4664
- [1, z.cx, z.cy],
4767
+ [1, z.cx, z.cy, 0],
4665
4768
  "none"
4666
4769
  );
4667
4770
  push2(start + style.rampIn * m, entry, spanEase(z.ease, style.ease));
4668
4771
  }
4772
+ let inPath = false;
4669
4773
  for (const e of z.followEvents ?? []) {
4670
4774
  const eOut = sourceToTimeline(segments, e.t);
4671
4775
  if (eOut === null || eOut <= tIn || eOut >= tOut) continue;
4672
- const next2 = [cur[0], e.cx, e.cy];
4673
- push2(eOut, cur, "none");
4674
- push2(Math.min(eOut + style.followRecenter, tOut), next2, panEase);
4776
+ const next2 = [cur[0], e.cx, e.cy, 1];
4777
+ if (e.path) {
4778
+ if (!inPath) {
4779
+ push2(Math.max(tIn, eOut - style.followRecenter), cur, "none");
4780
+ push2(eOut, next2, panEase);
4781
+ inPath = true;
4782
+ } else {
4783
+ push2(eOut, next2, "linear");
4784
+ }
4785
+ } else {
4786
+ inPath = false;
4787
+ push2(eOut, cur, "none");
4788
+ push2(Math.min(eOut + style.followRecenter, tOut), next2, panEase);
4789
+ }
4675
4790
  cur = next2;
4676
4791
  }
4677
4792
  push2(tOut, cur, "none");
4678
4793
  const next = mapped.at(i + 1);
4679
4794
  if (next && next.tIn - tOut <= style.chainGap) {
4680
4795
  const mNext = transitionMult(next.z.transition);
4681
- const nextValue = [clampZoomLevel(next.z.level), next.z.cx, next.z.cy];
4796
+ const nextValue = [clampZoomLevel(next.z.level), next.z.cx, next.z.cy, 1];
4682
4797
  push2(
4683
4798
  Math.min(
4684
4799
  tOut + style.pan * mNext,
@@ -4691,7 +4806,7 @@ function zoomTrackFromDoc(zoom, segments, style = ZOOM_STYLES[DEFAULT_ZOOM_STYLE
4691
4806
  } else {
4692
4807
  push2(
4693
4808
  tOut + style.rampOut * m,
4694
- [1, cur[1], cur[2]],
4809
+ [1, cur[1], cur[2], 0],
4695
4810
  spanEase(z.ease, style.ease)
4696
4811
  );
4697
4812
  chained = false;
@@ -5873,11 +5988,15 @@ var ON_FRAME = `(ctx, content, dt) => {
5873
5988
  // current zoom \u2014 a standard keyframe track in OUTPUT time (hold + arrival pairs
5874
5989
  // expanded by the lowering), sampled with the shared deterministic interpolator.
5875
5990
  // Sampled here, before the rect math, so a cover crop can follow it.
5876
- var lvl = 1, zx = 0.5, zy = 0.5
5991
+ var lvl = 1, zx = 0.5, zy = 0.5, zc = -1
5877
5992
  var zt = d.zoomTrack
5878
5993
  if (zt && zt.keyframes && zt.keyframes.length) {
5879
5994
  var z = TL.sample(zt, tpT, TL.lerpArray)
5880
5995
  lvl = z[0]; zx = z[1]; zy = z[2]
5996
+ // The stage camera's centring rides the track (0 rest, 1 apex, eased
5997
+ // with the level); a three-component track (a document lowered before
5998
+ // the component existed) falls back to the level band below.
5999
+ if (z.length > 3) zc = z[3]
5881
6000
  }
5882
6001
  var barH = bar.kind && bar.kind !== 'none' ? (bar.height || 44) * s2 : 0
5883
6002
  var availW = Math.max(1, W - ipL - ipR), availH = Math.max(1, H - ipT - ipB - barH)
@@ -5940,8 +6059,11 @@ var ON_FRAME = `(ctx, content, dt) => {
5940
6059
  var wcx = fx, wcy = fy
5941
6060
  if (lvl > 1.001 && !d.zoomSuppressed) {
5942
6061
  if (camStage) {
5943
- var ctu = Math.max(0, Math.min(1, (lvl - 1) / ${CAMERA_CENTRE_RAMP}))
5944
- var ctt = ctu * ctu * (3 - 2 * ctu)
6062
+ var ctt = zc
6063
+ if (ctt < 0) {
6064
+ var ctu = Math.max(0, Math.min(1, (lvl - 1) / ${CAMERA_CENTRE_RAMP}))
6065
+ ctt = ctu * ctu * (3 - 2 * ctu)
6066
+ }
5945
6067
  wcx = fx + ((W / 2 - fx) / lvl) * (1 - ctt)
5946
6068
  wcy = fy + ((H / 2 - fy) / lvl) * (1 - ctt)
5947
6069
  c.translate(W / 2, H / 2); c.scale(lvl, lvl); c.translate(-wcx, -wcy)
@@ -9995,6 +10117,8 @@ export {
9995
10117
  DIGEST_VERSION,
9996
10118
  DOC_SCHEMA_VERSION,
9997
10119
  DRAG_FIT_LEVEL,
10120
+ DRAG_MIN_S,
10121
+ DRAG_MIN_TRAVEL_FRAC,
9998
10122
  END_CARD_FROM,
9999
10123
  END_CARD_RECEDE,
10000
10124
  END_CARD_SECONDS,
@@ -10145,6 +10269,7 @@ export {
10145
10269
  docToCropSpace,
10146
10270
  docToFullSpace,
10147
10271
  docTransitions,
10272
+ dragsFromTrack,
10148
10273
  dropTemplate,
10149
10274
  duckCurve,
10150
10275
  effectiveSegments,