@vosjs/studio-core 0.21.0 → 0.22.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
@@ -656,7 +656,15 @@ interface CursorStyle {
656
656
  smoothing: number;
657
657
  /** rendered cursor size in px. */
658
658
  size: number;
659
- style: 'default' | 'dot' | 'ring';
659
+ /**
660
+ * What the pointer is drawn as. `'arrow'` is an OS-style pointer (a
661
+ * black arrow with a white edge, its tip on the recorded point), the
662
+ * default for new takes: it is the pointer the viewer's own screen wears,
663
+ * so the frame reads as a screen and not as a screen recorder's overlay.
664
+ * `'default'`, `'dot'` and `'ring'` all draw the white dot every earlier
665
+ * take was cut with. Click effects bloom under the tip either way.
666
+ */
667
+ style: 'default' | 'dot' | 'ring' | 'arrow';
660
668
  hideWhenIdle: boolean;
661
669
  clickFx: ClickFxStyle;
662
670
  }
@@ -957,6 +965,20 @@ interface FrameStyle {
957
965
  cx: number;
958
966
  cy: number;
959
967
  };
968
+ /**
969
+ * The zoom camera's model. `'card'` (absent; every existing document) is
970
+ * the magnifier: the card scales about the focus point, which stays where
971
+ * it is on screen, and the lowering clamps the focus so the zoomed card
972
+ * always covers the canvas, which means a target near a card edge can
973
+ * never sit near the frame's centre. `'stage'` is a camera: the card
974
+ * scales AND slides so the focus lands at the frame's centre (blended in
975
+ * over the first CAMERA_CENTRE_RAMP of level, so level 1 is the identity),
976
+ * the focus is never clamped, and past the card's edge the frame shows
977
+ * the ground, radius and shadow, the way a filmed card reads. New takes
978
+ * open on `'stage'` (BASE_FRAME_STYLE); the layout helpers, the digest's
979
+ * framing window and the studio's aiming rect all read this field.
980
+ */
981
+ camera?: 'card' | 'stage';
960
982
  aspectRatio: string;
961
983
  browserBar: BrowserBarStyle;
962
984
  /**
@@ -2299,9 +2321,78 @@ interface FocusBounds {
2299
2321
  * the bounds cross — collapse to their midpoint (the least-uncovered focus;
2300
2322
  * 0.5 for a centered card, matching OpenScreen's margin collapse).
2301
2323
  */
2302
- declare function focusBounds(level: number, layout: CardLayout): FocusBounds;
2324
+ declare function focusBounds(level: number, layout: CardLayout, camera?: CameraModel): FocusBounds;
2303
2325
  /** Clamp a focus point into the bounds for its zoom level. */
2304
- declare function clampFocus(cx: number, cy: number, level: number, layout: CardLayout): {
2326
+ declare function clampFocus(cx: number, cy: number, level: number, layout: CardLayout, camera?: CameraModel): {
2327
+ cx: number;
2328
+ cy: number;
2329
+ };
2330
+ /**
2331
+ * The zoom camera's model (FrameStyle.camera): the clamped magnifier every
2332
+ * existing document was cut with, or the stage camera a new take opens on.
2333
+ */
2334
+ type CameraModel = 'card' | 'stage';
2335
+ /** The frame's camera model; absent = the magnifier. */
2336
+ declare function cameraModel(frame: {
2337
+ camera?: string | null;
2338
+ } | null | undefined): CameraModel;
2339
+ /**
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.
2344
+ */
2345
+ declare const CAMERA_CENTRE_RAMP = 0.3;
2346
+ /**
2347
+ * The fraction of the frame (each axis, centred) the card must still cover
2348
+ * at a stage-camera apex. 0.8 leaves a strip of ground a tenth of the
2349
+ * frame wide beside a corner target, the composition a filmed card reads
2350
+ * as; 1 would be the magnifier's rule, 0 no clamp at all.
2351
+ */
2352
+ declare const CAMERA_COVER_MIN = 0.8;
2353
+ /** 0..1: the stage camera's centring at `level` (MIRRORS ON_FRAME). */
2354
+ declare function cameraCentring(level: number): number;
2355
+ /**
2356
+ * The zoom transform at one instant, in canvas px: content point p maps to
2357
+ * `(ox + (p.x − wcx)·level, oy + (p.y − wcy)·level)`. `fx/fy` is the focus
2358
+ * in canvas px; `wcx/wcy` is the content point that lands at the frame's
2359
+ * centre `ox/oy`. Under the magnifier `wc = f + (o − f) / L`, which is the
2360
+ * old "scale about the focus" transform written the long way (the focus
2361
+ * stays where it is on screen); under the stage camera that pull toward
2362
+ * the focus fades out with cameraCentring, so `wc → f` and the target sits
2363
+ * at the centre. ON_FRAME's zoom block mirrors this — change them together
2364
+ * (layout.test.ts pins the pair).
2365
+ */
2366
+ interface ZoomView {
2367
+ level: number;
2368
+ fx: number;
2369
+ fy: number;
2370
+ wcx: number;
2371
+ wcy: number;
2372
+ ox: number;
2373
+ oy: number;
2374
+ }
2375
+ declare function zoomView(level: number, cx: number, cy: number, layout: CardLayout, camera?: CameraModel): ZoomView;
2376
+ /**
2377
+ * The visible window as fractions of the canvas (x/y its top-left, w/h its
2378
+ * size, both 1/level): what the studio's aiming rect draws and what the
2379
+ * framing lint tests a click rect against. The window may leave the card
2380
+ * under the stage camera; it never does under the magnifier once the focus
2381
+ * is clamped.
2382
+ */
2383
+ declare function zoomViewport(level: number, cx: number, cy: number, layout: CardLayout, camera?: CameraModel): {
2384
+ x: number;
2385
+ y: number;
2386
+ w: number;
2387
+ h: number;
2388
+ };
2389
+ /**
2390
+ * The inverse of zoomViewport's centre: the focus (normalized video coords)
2391
+ * that puts the visible window's centre at canvas fractions `cX/cY` at
2392
+ * `level`. The aiming rect's corner drag re-derives its focus through this
2393
+ * so the rect stays under the pointer in either camera model.
2394
+ */
2395
+ declare function focusForViewportCentre(cX: number, cY: number, level: number, layout: CardLayout, camera?: CameraModel): {
2305
2396
  cx: number;
2306
2397
  cy: number;
2307
2398
  };
@@ -2320,12 +2411,16 @@ interface ZoomWindow {
2320
2411
  y0: number;
2321
2412
  y1: number;
2322
2413
  }
2323
- /** The visible window in normalized video coords at `level` around the focus. */
2414
+ /**
2415
+ * The visible window in normalized video coords at `level` around the
2416
+ * focus, under the frame's camera model (absent = the magnifier, whose
2417
+ * focus is clamped first exactly as the lowering clamps it).
2418
+ */
2324
2419
  declare function zoomWindow(span: {
2325
2420
  level: number;
2326
2421
  cx: number;
2327
2422
  cy: number;
2328
- }, layout: CardLayout): ZoomWindow;
2423
+ }, layout: CardLayout, camera?: CameraModel): ZoomWindow;
2329
2424
  /**
2330
2425
  * True when the rect (normalized) sits inside the zoom's visible window, with
2331
2426
  * `tol` of slack per edge (frame fractions). A level ≤ 1 zoom shows the whole
@@ -2335,7 +2430,7 @@ declare function zoomCoversRect(span: {
2335
2430
  level: number;
2336
2431
  cx: number;
2337
2432
  cy: number;
2338
- }, rect: NormRect, layout: CardLayout, tol?: number): boolean;
2433
+ }, rect: NormRect, layout: CardLayout, tol?: number, camera?: CameraModel): boolean;
2339
2434
 
2340
2435
  declare const STYLE_FIELDS: readonly ["zoomStyle", "zoomParams", "speedParams", "tiltStyle", "frame", "cursor", "cam", "export"];
2341
2436
  type StyleField = (typeof STYLE_FIELDS)[number];
@@ -2734,6 +2829,8 @@ interface FollowOptions {
2734
2829
  recenter?: number;
2735
2830
  /** target the cursor this many seconds ahead of the exit moment. */
2736
2831
  lookahead?: number;
2832
+ /** the frame's camera model: the stage camera never clamps a focus. */
2833
+ camera?: CameraModel;
2737
2834
  }
2738
2835
  interface FollowEvent {
2739
2836
  /** SOURCE seconds — the moment the recenter starts. */
@@ -4354,4 +4451,4 @@ declare function rejectSpan(rejected: RejectedSpan[] | undefined, lane: Rejected
4354
4451
  anchor?: StepAnchor;
4355
4452
  }, note?: string): RejectedSpan;
4356
4453
 
4357
- 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_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 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 ZoomWindow, anchorKindOf, anchorSourceDuration, animStep, applyLook, applyTemplate, aspectRatioValue, audioLane, backdropMedia, buildDigest, camBubbleRect, camBubbleRectAt, camLane, camMoveLane, camRectFromPose, camRestPose, camTrackFromDoc, 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, 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, zoomWindow };
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 };
package/dist/index.js CHANGED
@@ -132,6 +132,9 @@ var DEFAULT_CURSOR_STYLE = {
132
132
  visible: true,
133
133
  smoothing: 0.15,
134
134
  size: 24,
135
+ // The dot every earlier take was cut with; a NEW take opens on the
136
+ // arrow (projectFromArtifact sets it), so a document that stores
137
+ // 'default' keeps its dot.
135
138
  style: "default",
136
139
  hideWhenIdle: true,
137
140
  clickFx: DEFAULT_CLICK_FX
@@ -157,6 +160,10 @@ var BASE_FRAME_STYLE = {
157
160
  radius: 12,
158
161
  shadow: 0.4,
159
162
  border: 0,
163
+ // The stage camera: a new take's zoom centres its target and shows the
164
+ // ground past the card's edge. A document without the field keeps the
165
+ // clamped magnifier it was cut with (see FrameStyle.camera).
166
+ camera: "stage",
160
167
  // 'native' = the recording's own aspect ratio (meta.width/height). See ASPECT_RATIOS.
161
168
  aspectRatio: "native",
162
169
  browserBar: DEFAULT_BROWSER_BAR
@@ -431,7 +438,9 @@ function projectFromArtifact(artifact, videoUrl, opts = {}) {
431
438
  zoom: [],
432
439
  // planner runs once the doc is loaded (editor) or planned (CLI)
433
440
  audio: [],
434
- cursor: { ...DEFAULT_CURSOR_STYLE },
441
+ // A new take wears the OS pointer; DEFAULT_CURSOR_STYLE keeps the dot
442
+ // so a document that stores it renders as it was cut.
443
+ cursor: { ...DEFAULT_CURSOR_STYLE, style: "arrow" },
435
444
  cam: { ...DEFAULT_CAM_STYLE },
436
445
  frame: {
437
446
  ...baseFrame,
@@ -445,7 +454,9 @@ function projectFromArtifact(artifact, videoUrl, opts = {}) {
445
454
  url: pageDisplayUrl(artifact.meta.pageUrl)
446
455
  }
447
456
  },
448
- export: { resolution: "1080p", fps: 30, format: "mp4" }
457
+ // 60 fps: the footage duplicates frames, the camera does not, and a
458
+ // 0.5 s glide at 30 fps is fifteen visible steps.
459
+ export: { resolution: "1080p", fps: 60, format: "mp4" }
449
460
  };
450
461
  return { doc, videoUrl };
451
462
  }
@@ -1132,6 +1143,12 @@ function clamp(v, lo, hi) {
1132
1143
  // src/zoomStyle.ts
1133
1144
  var ZOOM_STYLES = {
1134
1145
  // Cursorful strategy — one steady zoom per activity session, travel by pans.
1146
+ // The camera's timing was measured against a Cursorful take of the same
1147
+ // steps: a zoom-in of ~0.55 s on a heavy ease-out (most of the move in
1148
+ // the first quarter second, a long settle), a zoom-out of ~0.45 s, and
1149
+ // holds of four to five seconds per beat. The old 1.1 s ramp started
1150
+ // 0.75 s BEFORE the click and landed 0.35 s after it, so the frame was
1151
+ // moving more of the time than it was resting.
1135
1152
  glide: {
1136
1153
  autoZoom: true,
1137
1154
  clusterGap: 3,
@@ -1139,22 +1156,22 @@ var ZOOM_STYLES = {
1139
1156
  targetFill: 0.42,
1140
1157
  minLevel: 1.3,
1141
1158
  maxLevel: 1.8,
1142
- lead: 0.5,
1143
- hold: 0.6,
1159
+ lead: 0.35,
1160
+ hold: 1.2,
1144
1161
  followByDefault: true,
1145
1162
  typingZoom: true,
1146
1163
  typingGap: 2.5,
1147
1164
  typingHold: 1.1,
1148
1165
  typingMinLevel: 1.4,
1149
- rampIn: 1.1,
1150
- rampInOverlap: 0.35,
1151
- rampOut: 1,
1152
- chainGap: 2.5,
1153
- pan: 1,
1154
- ease: "css-bezier(0.26, 0, 0.16, 1)",
1155
- panEase: "css-bezier(0.3, 0, 0.2, 1)",
1166
+ rampIn: 0.55,
1167
+ rampInOverlap: 0.2,
1168
+ rampOut: 0.5,
1169
+ chainGap: 3,
1170
+ pan: 0.55,
1171
+ ease: "css-bezier(0.16, 1, 0.3, 1)",
1172
+ panEase: "css-bezier(0.22, 1, 0.36, 1)",
1156
1173
  followSafeRatio: 0.45,
1157
- followRecenter: 0.8,
1174
+ followRecenter: 0.55,
1158
1175
  followLookahead: 0.4,
1159
1176
  tilt: { intensity: "off" }
1160
1177
  },
@@ -1865,10 +1882,29 @@ function camBubbleRect(cam, W, H = 1080) {
1865
1882
  radius: cam.shape === "rounded" ? (cam.radius ?? 18) * s : size / 2
1866
1883
  };
1867
1884
  }
1868
- function focusBounds(level, layout) {
1885
+ function focusBounds(level, layout, camera = "card") {
1869
1886
  if (level <= 1.001) {
1870
1887
  return { minX: 0.5, maxX: 0.5, minY: 0.5, maxY: 0.5 };
1871
1888
  }
1889
+ if (camera === "stage") {
1890
+ const x2 = stageAxisBounds(
1891
+ layout.dx,
1892
+ layout.dw,
1893
+ layout.cardX,
1894
+ layout.cardW,
1895
+ layout.W,
1896
+ level
1897
+ );
1898
+ const y2 = stageAxisBounds(
1899
+ layout.dy,
1900
+ layout.dh,
1901
+ layout.cardY,
1902
+ layout.cardH,
1903
+ layout.H,
1904
+ level
1905
+ );
1906
+ return { minX: x2.min, maxX: x2.max, minY: y2.min, maxY: y2.max };
1907
+ }
1872
1908
  const x = axisBounds(
1873
1909
  layout.dx,
1874
1910
  layout.dw,
@@ -1898,8 +1934,19 @@ function axisBounds(anchorOff, anchorSize, coverOff, coverSize, viewport, level)
1898
1934
  }
1899
1935
  return { min: clamp013(lo), max: clamp013(hi) };
1900
1936
  }
1901
- function clampFocus(cx, cy, level, layout) {
1902
- const b = focusBounds(level, layout);
1937
+ function stageAxisBounds(anchorOff, anchorSize, coverOff, coverSize, viewport, level) {
1938
+ const b = CAMERA_COVER_MIN / 2 * viewport;
1939
+ let lo = (coverOff + b / level - anchorOff) / anchorSize;
1940
+ let hi = (coverOff + coverSize - b / level - anchorOff) / anchorSize;
1941
+ if (lo > hi) {
1942
+ const mid = (lo + hi) / 2;
1943
+ lo = mid;
1944
+ hi = mid;
1945
+ }
1946
+ return { min: clamp013(lo), max: clamp013(hi) };
1947
+ }
1948
+ function clampFocus(cx, cy, level, layout, camera = "card") {
1949
+ const b = focusBounds(level, layout, camera);
1903
1950
  return {
1904
1951
  cx: Math.min(b.maxX, Math.max(b.minX, cx)),
1905
1952
  cy: Math.min(b.maxY, Math.max(b.minY, cy))
@@ -1908,6 +1955,45 @@ function clampFocus(cx, cy, level, layout) {
1908
1955
  function clamp013(v) {
1909
1956
  return Math.max(0, Math.min(1, v));
1910
1957
  }
1958
+ function cameraModel(frame) {
1959
+ return frame?.camera === "stage" ? "stage" : "card";
1960
+ }
1961
+ var CAMERA_CENTRE_RAMP = 0.3;
1962
+ var CAMERA_COVER_MIN = 0.8;
1963
+ function cameraCentring(level) {
1964
+ const u = clamp013((level - 1) / CAMERA_CENTRE_RAMP);
1965
+ return u * u * (3 - 2 * u);
1966
+ }
1967
+ function zoomView(level, cx, cy, layout, camera = "card") {
1968
+ const L = Math.max(1, level);
1969
+ const fx = layout.dx + cx * layout.dw;
1970
+ const fy = layout.dy + cy * layout.dh;
1971
+ const ox = layout.W / 2;
1972
+ const oy = layout.H / 2;
1973
+ const t = camera === "stage" ? cameraCentring(L) : 0;
1974
+ const wcx = fx + (ox - fx) / L * (1 - t);
1975
+ const wcy = fy + (oy - fy) / L * (1 - t);
1976
+ return { level: L, fx, fy, wcx, wcy, ox, oy };
1977
+ }
1978
+ function zoomViewport(level, cx, cy, layout, camera = "card") {
1979
+ const v = zoomView(level, cx, cy, layout, camera);
1980
+ const size = 1 / v.level;
1981
+ return {
1982
+ x: (v.wcx - layout.W / (2 * v.level)) / layout.W,
1983
+ y: (v.wcy - layout.H / (2 * v.level)) / layout.H,
1984
+ w: size,
1985
+ h: size
1986
+ };
1987
+ }
1988
+ function focusForViewportCentre(cX, cY, level, layout, camera = "card") {
1989
+ const L = Math.max(1, level);
1990
+ const t = camera === "stage" ? cameraCentring(L) : 0;
1991
+ const a = 1 - (1 - t) / L;
1992
+ if (a <= 1e-6) return { cx: 0.5, cy: 0.5 };
1993
+ const fx = (cX * layout.W - layout.W / 2 * (1 - t) / L) / a;
1994
+ const fy = (cY * layout.H - layout.H / 2 * (1 - t) / L) / a;
1995
+ return { cx: (fx - layout.dx) / layout.dw, cy: (fy - layout.dy) / layout.dh };
1996
+ }
1911
1997
  function levelForFocusFraction(frac) {
1912
1998
  if (!(frac > 0)) return clampZoomLevel(Number.POSITIVE_INFINITY);
1913
1999
  return clampZoomLevel(Math.max(1.1, 1 / frac));
@@ -2812,6 +2898,7 @@ function followFocusEvents(span, cursor, space, layout, options = {}) {
2812
2898
  const safeRatio = options.safeRatio ?? FOLLOW_SAFE_RATIO;
2813
2899
  const recenter = options.recenter ?? FOLLOW_RECENTER;
2814
2900
  const lookahead = options.lookahead ?? 0;
2901
+ const camera = options.camera ?? "card";
2815
2902
  const level = clampZoomLevel(span.level);
2816
2903
  if (!cursor.length || !space.w || !space.h || level <= 1.001) {
2817
2904
  return { entry: null, events: [] };
@@ -2822,12 +2909,7 @@ function followFocusEvents(span, cursor, space, layout, options = {}) {
2822
2909
  ny: clamp014(e.y / space.h)
2823
2910
  }));
2824
2911
  if (!pts.length) return { entry: null, events: [] };
2825
- let entryPt = pts[0];
2826
- for (const p of pts) {
2827
- if (p.t > span.in) break;
2828
- entryPt = p;
2829
- }
2830
- const entry = clampFocus(entryPt.nx, entryPt.ny, level, layout);
2912
+ const entry = clampFocus(span.cx, span.cy, level, layout, camera);
2831
2913
  const thrX = safeRatio * layout.W / (2 * level * layout.dw);
2832
2914
  const thrY = safeRatio * layout.H / (2 * level * layout.dh);
2833
2915
  const events = [];
@@ -2840,7 +2922,7 @@ function followFocusEvents(span, cursor, space, layout, options = {}) {
2840
2922
  if (p.t < nextAllowed) continue;
2841
2923
  if (Math.abs(p.nx - cx) > thrX || Math.abs(p.ny - cy) > thrY) {
2842
2924
  const target = lookahead > 0 ? sampleAt2(pts, Math.min(p.t + lookahead, span.out)) : p;
2843
- const f = clampFocus(target.nx, target.ny, level, layout);
2925
+ const f = clampFocus(target.nx, target.ny, level, layout, camera);
2844
2926
  if (Math.abs(f.cx - cx) < 1e-3 && Math.abs(f.cy - cy) < 1e-3) continue;
2845
2927
  events.push({ t: round2(p.t), cx: round2(f.cx), cy: round2(f.cy) });
2846
2928
  cx = f.cx;
@@ -5844,9 +5926,28 @@ var ON_FRAME = `(ctx, content, dt) => {
5844
5926
  // ceZs = the zoom transform's device scale, for the two values below that
5845
5927
  // are sized in DEVICE pixels inside a scaled user space.
5846
5928
  var ceZs = 1
5929
+ // The camera model (cfr.camera; MIRRORS layout.ts zoomView \u2014 change them
5930
+ // together). The magnifier scales the card about the focus, which stays
5931
+ // where it is on screen; the stage camera also slides the card so the
5932
+ // focus lands at the frame's centre, blended in over CAMERA_CENTRE_RAMP
5933
+ // of level (a smoothstep, 0 at level 1) so a ramp through the band slides
5934
+ // without a kink, and past the card's edge the ground shows. wcx/wcy is
5935
+ // the content point at the frame's centre under either model; the two
5936
+ // branches are the same map, the magnifier's kept in its old float order
5937
+ // so every existing document renders byte-identically.
5938
+ var camStage = cfr.camera === 'stage'
5939
+ var fx = dx + zx * dw, fy = dy + zy * dh
5940
+ var wcx = fx, wcy = fy
5847
5941
  if (lvl > 1.001 && !d.zoomSuppressed) {
5848
- var fx = dx + zx * dw, fy = dy + zy * dh
5849
- c.translate(fx, fy); c.scale(lvl, lvl); c.translate(-fx, -fy)
5942
+ if (camStage) {
5943
+ var ctu = Math.max(0, Math.min(1, (lvl - 1) / ${CAMERA_CENTRE_RAMP}))
5944
+ var ctt = ctu * ctu * (3 - 2 * ctu)
5945
+ wcx = fx + ((W / 2 - fx) / lvl) * (1 - ctt)
5946
+ wcy = fy + ((H / 2 - fy) / lvl) * (1 - ctt)
5947
+ c.translate(W / 2, H / 2); c.scale(lvl, lvl); c.translate(-wcx, -wcy)
5948
+ } else {
5949
+ c.translate(fx, fy); c.scale(lvl, lvl); c.translate(-fx, -fy)
5950
+ }
5850
5951
  ceZs = lvl
5851
5952
  }
5852
5953
  // The card's shadows are cast by a body drawn OFF-CANVAS and brought back
@@ -5988,6 +6089,34 @@ var ON_FRAME = `(ctx, content, dt) => {
5988
6089
  // cursor coordinate space + drawn radius (shared by click effects + the dot)
5989
6090
  var space = (actM ? actM.cursorSpace : d.cursorSpace) || { w: vw, h: vh }
5990
6091
  var curSize = ((d.cursorStyle && d.cursorStyle.size) || 24) * s2 * 0.5
6092
+ // The pointer's shape: an OS-style arrow (tip on the recorded point, a
6093
+ // black body with a white edge, the shape of the pointer on the viewer's
6094
+ // own screen) or the white dot every earlier take was cut with. One
6095
+ // painter for both draw sites (in the card, and the ghost across a
6096
+ // boundary); r is the dot's radius, so an arrow stands 2r tall.
6097
+ var curArrow = !!(d.cursorStyle && d.cursorStyle.style === 'arrow')
6098
+ function drawCur(cc, x, y, r, a, lw) {
6099
+ cc.save()
6100
+ if (curArrow) {
6101
+ var h = 2 * r
6102
+ var P = [[0, 0], [0, 0.72], [0.19, 0.55], [0.31, 0.83], [0.43, 0.78], [0.31, 0.5], [0.55, 0.5]]
6103
+ cc.beginPath()
6104
+ cc.moveTo(x, y)
6105
+ for (var pi = 1; pi < P.length; pi++) cc.lineTo(x + P[pi][0] * h, y + P[pi][1] * h)
6106
+ cc.closePath()
6107
+ cc.lineJoin = 'round'
6108
+ cc.fillStyle = 'rgba(0,0,0,' + (0.92 * a) + ')'
6109
+ cc.strokeStyle = 'rgba(255,255,255,' + (0.95 * a) + ')'
6110
+ cc.lineWidth = Math.max(1, lw * 0.75)
6111
+ cc.fill(); cc.stroke()
6112
+ } else {
6113
+ cc.fillStyle = 'rgba(255,255,255,' + (0.95 * a) + ')'
6114
+ cc.strokeStyle = 'rgba(0,0,0,' + (0.4 * a) + ')'
6115
+ cc.lineWidth = lw
6116
+ cc.beginPath(); cc.arc(x, y, r, 0, Math.PI * 2); cc.fill(); cc.stroke()
6117
+ }
6118
+ cc.restore()
6119
+ }
5991
6120
 
5992
6121
  // click effects \u2014 pure f(t): d.clicks are
5993
6122
  // OUTPUT-anchored records baked at lowering (sorted by ot; re-baked on every
@@ -6161,16 +6290,14 @@ var ON_FRAME = `(ctx, content, dt) => {
6161
6290
  // moving: each pass leaves its point, mapped out of the zoom.
6162
6291
  if (trOut) {
6163
6292
  var tqX = ax, tqY = ay
6164
- if (lvl > 1.001 && !d.zoomSuppressed) { tqX = fx + (ax - fx) * lvl; tqY = fy + (ay - fy) * lvl }
6293
+ if (lvl > 1.001 && !d.zoomSuppressed) {
6294
+ if (camStage) { tqX = W / 2 + (ax - wcx) * lvl; tqY = H / 2 + (ay - wcy) * lvl }
6295
+ else { tqX = fx + (ax - fx) * lvl; tqY = fy + (ay - fy) * lvl }
6296
+ }
6165
6297
  if (tpG) trQa = [tqX, tqY]
6166
6298
  else { trQb = [tqX, tqY]; trQs = curSize * ckPress; trQw = s2 }
6167
6299
  } else if (cuA > 0.01) {
6168
- c.save()
6169
- c.fillStyle = 'rgba(255,255,255,' + (0.95 * cuA) + ')'
6170
- c.strokeStyle = 'rgba(0,0,0,' + (0.4 * cuA) + ')'
6171
- c.lineWidth = 2 * s2
6172
- c.beginPath(); c.arc(ax, ay, curSize * ckPress, 0, Math.PI * 2); c.fill(); c.stroke()
6173
- c.restore()
6300
+ drawCur(c, ax, ay, curSize * ckPress, cuA, 2 * s2)
6174
6301
  }
6175
6302
  }
6176
6303
  if (fitCover) c.restore()
@@ -6255,12 +6382,7 @@ var ON_FRAME = `(ctx, content, dt) => {
6255
6382
  var tqBx = (tqB[0] - W / 2) * trSc + W / 2 + trOx * W, tqBy = (tqB[1] - H / 2) * trSc + H / 2 - trOy * H
6256
6383
  var tqX2 = tqAx + (tqBx - tqAx) * tqP, tqY2 = tqAy + (tqBy - tqAy) * tqP
6257
6384
  var tqR = trQs || (((d.cursorStyle && d.cursorStyle.size) || 24) * s * 0.5)
6258
- ovC.save()
6259
- ovC.fillStyle = 'rgba(255,255,255,0.95)'
6260
- ovC.strokeStyle = 'rgba(0,0,0,0.4)'
6261
- ovC.lineWidth = 2 * (trQw || s)
6262
- ovC.beginPath(); ovC.arc(tqX2, tqY2, tqR, 0, Math.PI * 2); ovC.fill(); ovC.stroke()
6263
- ovC.restore()
6385
+ drawCur(ovC, tqX2, tqY2, tqR, 1, 2 * (trQw || s))
6264
6386
  }
6265
6387
  ov.sig = ovSig
6266
6388
  ov.active = camActiveNow || !!trOut
@@ -6599,6 +6721,7 @@ function lowerToComposition(input) {
6599
6721
  const layout = docCardLayout(doc);
6600
6722
  const meta = doc.source.meta;
6601
6723
  const zoomStyle = resolveZoomStyle(doc.zoomStyle, doc.zoomParams);
6724
+ const camera = cameraModel(doc.frame);
6602
6725
  const zoomSpans = restSpansThroughTransitions(
6603
6726
  doc.zoom,
6604
6727
  rated,
@@ -6614,13 +6737,17 @@ function lowerToComposition(input) {
6614
6737
  {
6615
6738
  safeRatio: zoomStyle.followSafeRatio,
6616
6739
  recenter: zoomStyle.followRecenter,
6617
- lookahead: zoomStyle.followLookahead
6740
+ lookahead: zoomStyle.followLookahead,
6741
+ camera
6618
6742
  }
6619
6743
  );
6620
6744
  if (f.entry)
6621
6745
  return { ...z, cx: f.entry.cx, cy: f.entry.cy, followEvents: f.events };
6622
6746
  }
6623
- return { ...z, ...clampFocus(z.cx, z.cy, clampZoomLevel(z.level), layout) };
6747
+ return {
6748
+ ...z,
6749
+ ...clampFocus(z.cx, z.cy, clampZoomLevel(z.level), layout, camera)
6750
+ };
6624
6751
  });
6625
6752
  const data = {
6626
6753
  videoSrc: doc.source.videoKey,
@@ -7176,15 +7303,14 @@ function sceneChanges(bins, opts = {}) {
7176
7303
  }
7177
7304
 
7178
7305
  // src/digest/framing.ts
7179
- function zoomWindow(span, layout) {
7306
+ function zoomWindow(span, layout, camera = "card") {
7180
7307
  const L = Math.max(1, span.level);
7181
- const { cx, cy } = clampFocus(span.cx, span.cy, L, layout);
7182
- const fx = layout.dx + cx * layout.dw;
7183
- const fy = layout.dy + cy * layout.dh;
7184
- const px0 = fx - fx / L;
7185
- const px1 = fx + (layout.W - fx) / L;
7186
- const py0 = fy - fy / L;
7187
- const py1 = fy + (layout.H - fy) / L;
7308
+ const { cx, cy } = clampFocus(span.cx, span.cy, L, layout, camera);
7309
+ const v = zoomViewport(L, cx, cy, layout, camera);
7310
+ const px0 = v.x * layout.W;
7311
+ const px1 = (v.x + v.w) * layout.W;
7312
+ const py0 = v.y * layout.H;
7313
+ const py1 = (v.y + v.h) * layout.H;
7188
7314
  return {
7189
7315
  x0: (px0 - layout.dx) / layout.dw,
7190
7316
  x1: (px1 - layout.dx) / layout.dw,
@@ -7192,9 +7318,9 @@ function zoomWindow(span, layout) {
7192
7318
  y1: (py1 - layout.dy) / layout.dh
7193
7319
  };
7194
7320
  }
7195
- function zoomCoversRect(span, rect, layout, tol = 0.02) {
7321
+ function zoomCoversRect(span, rect, layout, tol = 0.02, camera = "card") {
7196
7322
  if (span.level <= 1.001) return true;
7197
- const w = zoomWindow(span, layout);
7323
+ const w = zoomWindow(span, layout, camera);
7198
7324
  return rect.x >= w.x0 - tol && rect.x + rect.w <= w.x1 + tol && rect.y >= w.y0 - tol && rect.y + rect.h <= w.y1 + tol;
7199
7325
  }
7200
7326
 
@@ -9812,6 +9938,8 @@ export {
9812
9938
  AUDIO_PLAN_STEP,
9813
9939
  BACKGROUND_Z,
9814
9940
  BASE_FRAME_STYLE,
9941
+ CAMERA_CENTRE_RAMP,
9942
+ CAMERA_COVER_MIN,
9815
9943
  CAMERA_FAR,
9816
9944
  CAMERA_NEAR,
9817
9945
  CAM_CHAIN_GAP,
@@ -9978,6 +10106,8 @@ export {
9978
10106
  camRectFromPose,
9979
10107
  camRestPose,
9980
10108
  camTrackFromDoc,
10109
+ cameraCentring,
10110
+ cameraModel,
9981
10111
  cardEnter,
9982
10112
  cardExit,
9983
10113
  cardFields,
@@ -10032,6 +10162,7 @@ export {
10032
10162
  exportSizeFor,
10033
10163
  extractClicks,
10034
10164
  focusBounds,
10165
+ focusForViewportCentre,
10035
10166
  followFocusEvents,
10036
10167
  frameGeometry,
10037
10168
  freezeLane,
@@ -10159,6 +10290,8 @@ export {
10159
10290
  zoomLane,
10160
10291
  zoomSpanForRange,
10161
10292
  zoomTrackFromDoc,
10293
+ zoomView,
10294
+ zoomViewport,
10162
10295
  zoomWindow
10163
10296
  };
10164
10297
  //# sourceMappingURL=index.js.map