@vosjs/studio-core 0.9.0 → 0.10.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
@@ -2013,23 +2013,78 @@ declare function zoomCoversRect(span: {
2013
2013
  cy: number;
2014
2014
  }, rect: NormRect, layout: CardLayout, tol?: number): boolean;
2015
2015
 
2016
- /**
2017
- * Style as DATA: the fields of a signed-off take's doc that ARE its
2018
- * style — camera, speed, tilt personality, frame, cursor, cam bubble, export.
2019
- * `copyStyle` carries them onto another take deterministically, so a series
2020
- * shares them by construction and the recipe (CUT.md) only has to say what a
2021
- * number cannot. Never the spans, overlays or audio: those are the cut.
2022
- */
2023
-
2024
2016
  declare const STYLE_FIELDS: readonly ["zoomStyle", "zoomParams", "speedParams", "tiltStyle", "frame", "cursor", "cam", "export"];
2025
2017
  type StyleField = (typeof STYLE_FIELDS)[number];
2026
2018
  /** The style fields present on a doc, deep-cloned. */
2027
2019
  declare function pickStyle(doc: ProjectDoc): Partial<Pick<ProjectDoc, StyleField>>;
2020
+ interface CopyStyleOptions {
2021
+ /**
2022
+ * Carry the seed's LAYOUT too (`copyLayout`): its stage clips by id, its
2023
+ * rest lean and its trailing hold. Off by default, so a series cut keeps
2024
+ * copying style alone.
2025
+ */
2026
+ layout?: boolean;
2027
+ /** Passed through to `copyLayout`. */
2028
+ keys?: Record<string, string>;
2029
+ }
2028
2030
  /**
2029
2031
  * A new doc: `to` with `from`'s style fields. A field absent on `from` is
2030
2032
  * removed from the result (the seed's absence is a choice — the default).
2031
2033
  */
2032
- declare function copyStyle(from: ProjectDoc, to: ProjectDoc): ProjectDoc;
2034
+ declare function copyStyle(from: ProjectDoc, to: ProjectDoc, opts?: CopyStyleOptions): ProjectDoc;
2035
+ /** The frame fields a layout owns. Never `aspectRatio` or `browserBar`. */
2036
+ declare const LAYOUT_FRAME_FIELDS: readonly ["background", "padding", "radius", "shadow", "shadowContact", "shadowColor", "inset", "border", "borderWidth", "borderColor", "fit", "focus", "focusFollow", "parallax", "entrance"];
2037
+ /**
2038
+ * The id prefix an exemplar reserves for the clips that ARE its layout:
2039
+ * the words and the mark a poster places (`stage-title`, `stage-kicker`,
2040
+ * `stage-brand`, `stage-mark`). Every other clip is the take's own.
2041
+ */
2042
+ declare const STAGE_CLIP_PREFIX = "stage-";
2043
+ /** The id of the whole-take lean a layout or a canvas gesture writes. */
2044
+ declare const REST_TILT_ID = "rest";
2045
+ declare const isStageClip: (clip: Pick<OverlayClip, "id">) => boolean;
2046
+ /** The exemplar's rest lean: its `rest`/`stage` span, or a span covering (nearly) the whole source. */
2047
+ declare function restLeanOf(doc: ProjectDoc): {
2048
+ rx: number;
2049
+ ry: number;
2050
+ } | null;
2051
+ /** What a document carries of a layout, for a report. */
2052
+ interface LayoutParts {
2053
+ /** The layout-owned frame fields present on the document. */
2054
+ frame: string[];
2055
+ /** The stage clips' ids. */
2056
+ clips: string[];
2057
+ lean: boolean;
2058
+ hold: boolean;
2059
+ }
2060
+ declare function layoutOf(doc: ProjectDoc): LayoutParts;
2061
+ interface CopyLayoutOptions {
2062
+ /**
2063
+ * Media keys this document should show in the exemplar's image clips,
2064
+ * by clip id (`{ 'stage-mark': 'brand/mark.svg' }`): an exemplar's mark
2065
+ * is its brand's, so without a key of its own (here, or already on the
2066
+ * document) an image stage clip is left out and said in `notes`.
2067
+ */
2068
+ keys?: Record<string, string>;
2069
+ }
2070
+ interface CopiedLayout {
2071
+ doc: ProjectDoc;
2072
+ /** What the copy left alone, in plain words (empty when nothing was). */
2073
+ notes: string[];
2074
+ }
2075
+ /**
2076
+ * `to` with `from`'s layout. Pure: returns a new document; never mutates
2077
+ * either input.
2078
+ *
2079
+ * Copied: the layout-owned frame fields (present on the exemplar ⇒ copied,
2080
+ * absent ⇒ removed; the animated ground rides along only when the exemplar
2081
+ * has one), the stage clips by id (a same-id text clip keeps the words this
2082
+ * document already has; an image clip keeps this document's key, or takes
2083
+ * one from `keys`), the rest lean as one whole-take span where this
2084
+ * document has no tilt spans of its own, and the trailing hold. Kept: the
2085
+ * aspect, the browser chrome, every clip that is not a stage clip, the cut.
2086
+ */
2087
+ declare function copyLayout(from: ProjectDoc, to: ProjectDoc, opts?: CopyLayoutOptions): CopiedLayout;
2033
2088
 
2034
2089
  /**
2035
2090
  * The digest's crop geometry, pure and shared by the CLI's page and
@@ -2750,6 +2805,17 @@ declare function applyLook(frame: FrameStyle, look: Look, size: {
2750
2805
  h: number;
2751
2806
  }, placement: LookPlacement): FrameStyle;
2752
2807
 
2808
+ /**
2809
+ * THE REST of a take: the one output time its still is taken at. A poster
2810
+ * ends on a trailing `hold` (its last segment freezes on its last frame),
2811
+ * and the rest is where that hold BEGINS — the composed frame before
2812
+ * nothing moves. Null when the take has no trailing hold, and null with an
2813
+ * end card: the end card grows that hold at lowering and its words rise
2814
+ * over the receding card during it, so the frame where it begins is
2815
+ * footage under a fading title, not a poster. One convention for every
2816
+ * still-taking surface (an export, a thumbnail, a kit), so they agree.
2817
+ */
2818
+ declare function docRestTime(doc: ProjectDoc): number | null;
2753
2819
  /** The entrance's default length, seconds. */
2754
2820
  declare const ENTRANCE_SECONDS = 1.2;
2755
2821
  /** The end card's default length, seconds. */
@@ -3344,4 +3410,4 @@ declare function rejectSpan(rejected: RejectedSpan[] | undefined, lane: Rejected
3344
3410
  anchor?: StepAnchor;
3345
3411
  }, note?: string): RejectedSpan;
3346
3412
 
3347
- export { ASPECT_RATIOS, AUDIO_PLAN_STEP, type AnchorKind, 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_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 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, 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_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, type FocusBounds, type FollowEvent, type FollowOptions, type FrameEntrance, type FrameGeometry, type FrameStyle, HOUSE_GRADIENT, type IngestOptions, LOOK_KINDS, type Look, type LookBrand, type LookKind, type LookPlacement, type LowerProgramOptions, type LoweredAudioClip, type LoweredClick, type LoweredComposition, type LoweredZoomSpan, MINIMAL_BAR_THEMES, MOTION_DELTA, MOTION_EASE, type MediaOverlayClip, 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, PROGRAM_RETIME, type PlanOptions, type PlanTiltOptions, type PlaneSize, type ProgramAnchorDoc, type ProgramTweenEdit, type ProjectDoc, type PxRect, REJECT_OVERLAP, type RecordingArtifact, type RecordingMeta, type Rect, type RejectedLane, type RejectedSpan, type ResolvedOverlayBox, type ResolvedOverlayStyle, SCENE_MOTION, SCENE_QUIET, SPEED_RATE_MAX, SPEED_RATE_MIN, SPEED_SPAN_MIN, STUDIO_ENTRY_ID, STYLE_FIELDS, 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_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_SPEED_MULT, 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 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, applyLook, aspectRatioValue, audioLane, backdropMedia, buildDigest, camBubbleRect, camBubbleRectAt, camLane, camMoveLane, camRectFromPose, camRestPose, camTrackFromDoc, cardInset, cardPointToScreen, cardPoseTrack, clampCamFrac, clampCamSize, clampFocus, clampSpeedRate, clampTiltDeg, clampZoomLevel, clipEnvelope, clipLength, computeCardLayout, computeMicRms, computePeaks, copyStyle, cropBox, cursorIdleFade, deriveViewportCrop, destinationById, destinationsForChannel, docCardLayout, docOutputDuration, docToCropSpace, docToFullSpace, duckCurve, effectiveSegments, entranceSeconds, envelopeAt, envelopeValueAt, expandEndCard, expectedFrameSize, exportSizeFor, extractClicks, focusBounds, followFocusEvents, frameGeometry, hexToRgbTriplet, houseLook, idleGaps, isLookKind, isMusicBed, isPlayback, isProgramDoc, isRecordingDoc, isRejected, levelForFocusFraction, lookFromBrand, lookKindForGround, lowerProgramDoc, lowerStudioDoc, lowerToComposition, micLane, migrateHostedDoc, momentsFromDoc, motionTrack, musicBedClip, normalizeCaptureSpace, objectMotionBase, objectMotionPoseAt, objectsLane, outputDurationOf, outputRangeToSource, overlapFraction, overlayFaceFor, overlayFontFaces, overlayFontString, overlayHit, overlayLines, overlayMotionBase, overlayMotionPoseAt, overlayRect, overlaysLane, pageDisplayUrl, parsePoseId, pickStyle, planAutoSpeed, planAutoTilt, planAutoZoom, planForDigest, planeSizeAtDepth, platformBarKind, programDuration, projectFromArtifact, ratedSegments, recommendedExportResolution, refillAudioBeds, rejectSpan, removeSourceRange, removeSpeedInRange, resolveExportSize, resolveOverlayBox, resolveOverlayStyle, resolveText3dAsset, resolveZoomStyle, sceneChanges, scrollRuns, setSpeedInRange, smoothCursor, spanOutputExtent, speedLane, studioAudioPlan, studioEntry, studioLayerData, tiltLane, tiltTrackFromDoc, transitionMult, videoLane, voiceKey, withBackdrop, withHolds, withoutRejected, wrapProgramLength, zoomCoversRect, zoomLane, zoomSpanForRange, zoomTrackFromDoc, zoomWindow };
3413
+ export { ASPECT_RATIOS, AUDIO_PLAN_STEP, type AnchorKind, 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_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, 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_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, type FocusBounds, type FollowEvent, type FollowOptions, type FrameEntrance, type FrameGeometry, type FrameStyle, HOUSE_GRADIENT, type IngestOptions, LAYOUT_FRAME_FIELDS, LOOK_KINDS, type LayoutParts, type Look, type LookBrand, type LookKind, type LookPlacement, type LowerProgramOptions, type LoweredAudioClip, type LoweredClick, type LoweredComposition, type LoweredZoomSpan, MINIMAL_BAR_THEMES, MOTION_DELTA, MOTION_EASE, type MediaOverlayClip, 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, 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, SPEED_RATE_MAX, SPEED_RATE_MIN, SPEED_SPAN_MIN, STAGE_CLIP_PREFIX, STUDIO_ENTRY_ID, STYLE_FIELDS, 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_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_SPEED_MULT, 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 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, applyLook, aspectRatioValue, audioLane, backdropMedia, buildDigest, camBubbleRect, camBubbleRectAt, camLane, camMoveLane, camRectFromPose, camRestPose, camTrackFromDoc, cardInset, cardPointToScreen, cardPoseTrack, clampCamFrac, clampCamSize, clampFocus, clampSpeedRate, clampTiltDeg, clampZoomLevel, clipEnvelope, clipLength, computeCardLayout, computeMicRms, computePeaks, copyLayout, copyStyle, cropBox, cursorIdleFade, deriveViewportCrop, destinationById, destinationsForChannel, docCardLayout, docOutputDuration, docRestTime, docToCropSpace, docToFullSpace, duckCurve, effectiveSegments, entranceSeconds, envelopeAt, envelopeValueAt, expandEndCard, expectedFrameSize, exportSizeFor, extractClicks, focusBounds, followFocusEvents, frameGeometry, hexToRgbTriplet, houseLook, idleGaps, isLookKind, isMusicBed, isPlayback, isProgramDoc, isRecordingDoc, isRejected, isStageClip, layoutOf, levelForFocusFraction, lookFromBrand, lookKindForGround, lowerProgramDoc, lowerStudioDoc, lowerToComposition, micLane, migrateHostedDoc, momentsFromDoc, motionTrack, musicBedClip, normalizeCaptureSpace, objectMotionBase, objectMotionPoseAt, objectsLane, outputDurationOf, outputRangeToSource, overlapFraction, overlayFaceFor, overlayFontFaces, overlayFontString, overlayHit, overlayLines, overlayMotionBase, overlayMotionPoseAt, overlayRect, overlaysLane, pageDisplayUrl, parsePoseId, pickStyle, planAutoSpeed, planAutoTilt, planAutoZoom, planForDigest, planeSizeAtDepth, platformBarKind, programDuration, projectFromArtifact, ratedSegments, recommendedExportResolution, refillAudioBeds, rejectSpan, removeSourceRange, removeSpeedInRange, resolveExportSize, resolveOverlayBox, resolveOverlayStyle, resolveText3dAsset, resolveZoomStyle, restLeanOf, sceneChanges, scrollRuns, setSpeedInRange, smoothCursor, spanOutputExtent, speedLane, studioAudioPlan, studioEntry, studioLayerData, tiltLane, tiltTrackFromDoc, transitionMult, videoLane, voiceKey, withBackdrop, withHolds, withoutRejected, wrapProgramLength, zoomCoversRect, zoomLane, zoomSpanForRange, zoomTrackFromDoc, zoomWindow };
package/dist/index.js CHANGED
@@ -1102,8 +1102,8 @@ import {
1102
1102
  segmentRate as segmentRate2,
1103
1103
  sortKeyframes as sortKeyframes2,
1104
1104
  sourceToTimeline as sourceToTimeline2,
1105
- splitBySpeed,
1106
- totalDuration
1105
+ splitBySpeed as splitBySpeed2,
1106
+ totalDuration as totalDuration2
1107
1107
  } from "@vosjs/timeline";
1108
1108
  import { timelineRuntimeCode as timelineRuntimeCode2 } from "@vosjs/timeline/bundle";
1109
1109
 
@@ -2774,6 +2774,20 @@ var STUDIO_FRAME = `(ctx, content, dt) => {
2774
2774
 
2775
2775
  // src/lower/motion.ts
2776
2776
  import { sortKeyframes } from "@vosjs/timeline";
2777
+ import { splitBySpeed, totalDuration } from "@vosjs/timeline";
2778
+ function docRestTime(doc) {
2779
+ if (doc.endCard) return null;
2780
+ const last = doc.segments.at(-1);
2781
+ const hold = last?.hold;
2782
+ if (typeof hold !== "number" || !(hold > 0)) return null;
2783
+ const segs = doc.segments.length ? doc.segments : [{ in: 0, out: doc.source.meta.durationMs / 1e3 }];
2784
+ const total = totalDuration(
2785
+ withHolds(segs, splitBySpeed(segs, doc.speed ?? []))
2786
+ );
2787
+ const rest = total - hold;
2788
+ if (!(rest >= 0)) return null;
2789
+ return Math.round(rest * 1e3) / 1e3;
2790
+ }
2777
2791
  var ENTRANCE_SECONDS = 1.2;
2778
2792
  var END_CARD_SECONDS = 2.5;
2779
2793
  var END_CARD_RECEDE = 0.7;
@@ -3020,10 +3034,10 @@ var CAM_PAN_EASE = ZOOM_PAN_EASE;
3020
3034
  var PROGRAM_DURATION = 1;
3021
3035
  function ratedSegments(doc) {
3022
3036
  const segs = isRecordingDoc(doc) ? doc.segments.length ? doc.segments : [{ in: 0, out: doc.source.meta.durationMs / 1e3 }] : [{ in: 0, out: programDuration(doc) }];
3023
- return withHolds(segs, splitBySpeed(segs, doc.speed ?? []));
3037
+ return withHolds(segs, splitBySpeed2(segs, doc.speed ?? []));
3024
3038
  }
3025
3039
  function durationSec(doc, rated) {
3026
- const trimmed = totalDuration(rated);
3040
+ const trimmed = totalDuration2(rated);
3027
3041
  return trimmed > 0 ? trimmed : doc.source.meta.durationMs / 1e3;
3028
3042
  }
3029
3043
  function spanOutputExtent(segments, sIn, sOut) {
@@ -3433,7 +3447,7 @@ var SETUP = `async (ctx) => {
3433
3447
  // exactly as it was; preview/scrub never sets the flag.
3434
3448
  const makeWcProvider = async (src) => {
3435
3449
  if (!window.VideoDecoder) return null
3436
- const MB = await import('https://esm.sh/mediabunny@1.27.3?target=es2022')
3450
+ const MB = await import('https://esm.sh/mediabunny@1.55.7?target=es2022')
3437
3451
  const wcBlob = ns.videoBlobs && ns.videoBlobs.get(src)
3438
3452
  const input = new MB.Input({
3439
3453
  formats: MB.ALL_FORMATS,
@@ -5278,6 +5292,88 @@ function zoomCoversRect(span, rect, layout, tol = 0.02) {
5278
5292
  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;
5279
5293
  }
5280
5294
 
5295
+ // src/audioBeds.ts
5296
+ import { totalDuration as totalDuration3 } from "@vosjs/timeline";
5297
+ var round32 = (v) => Math.round(v * 1e3) / 1e3;
5298
+ function docOutputDuration(doc) {
5299
+ return totalDuration3(ratedSegments(doc));
5300
+ }
5301
+ function musicBedClip(input) {
5302
+ const track = round32(input.trackDuration);
5303
+ const output = round32(input.outputDuration);
5304
+ const clip = {
5305
+ id: input.id,
5306
+ key: input.key,
5307
+ name: input.name,
5308
+ start: 0,
5309
+ in: 0,
5310
+ out: track,
5311
+ duration: track,
5312
+ // Under speech, not over it; catalog tracks are loudness-normalized so
5313
+ // one default means the same thing across the library.
5314
+ gain: 0.5,
5315
+ fadeIn: 0,
5316
+ // A bed that just stops reads as a glitch; clamp so a very short cut
5317
+ // still spends most of its time at full level.
5318
+ fadeOut: output > 0 ? Math.min(1.5, round32(output / 4)) : 1.5,
5319
+ duck: input.hasMic || void 0
5320
+ };
5321
+ if (output <= 0) return clip;
5322
+ if (track >= output) {
5323
+ clip.out = output;
5324
+ } else {
5325
+ clip.loop = true;
5326
+ clip.loopLen = output;
5327
+ }
5328
+ return clip;
5329
+ }
5330
+ function isMusicBed(clip, outputDuration) {
5331
+ if (clip.start > 0.05) return false;
5332
+ if (clip.loop) return true;
5333
+ const placed = clip.out - clip.in;
5334
+ return Math.abs(placed - outputDuration) <= 1;
5335
+ }
5336
+ function refillAudioBeds(doc, prevDuration, nextDuration) {
5337
+ const EPS2 = 0.05;
5338
+ if (Math.abs(nextDuration - prevDuration) <= EPS2 || nextDuration <= 0) {
5339
+ return false;
5340
+ }
5341
+ let changed = false;
5342
+ for (const clip of doc.audio) {
5343
+ if (clip.start >= nextDuration) continue;
5344
+ const placed = clip.loop ? Math.max(clip.out - clip.in, clip.loopLen ?? clip.out - clip.in) : clip.out - clip.in;
5345
+ if (Math.abs(clip.start + placed - prevDuration) > EPS2) continue;
5346
+ const span = clip.out - clip.in;
5347
+ const nextLen = round32(nextDuration - clip.start);
5348
+ if (clip.loop) {
5349
+ if (nextLen >= span) {
5350
+ clip.loopLen = nextLen;
5351
+ } else {
5352
+ clip.loop = void 0;
5353
+ clip.loopLen = void 0;
5354
+ clip.out = round32(clip.in + nextLen);
5355
+ }
5356
+ } else {
5357
+ if (clip.in + nextLen <= clip.duration) {
5358
+ clip.out = round32(clip.in + nextLen);
5359
+ } else {
5360
+ clip.out = clip.duration;
5361
+ clip.loop = true;
5362
+ clip.loopLen = nextLen;
5363
+ }
5364
+ }
5365
+ changed = true;
5366
+ }
5367
+ return changed;
5368
+ }
5369
+ function voiceKey(doc) {
5370
+ if (!("source" in doc)) return null;
5371
+ const src = doc.source;
5372
+ if (src.micKey) return src.micKey;
5373
+ if (src.meta.hasAudio && !src.meta.hasMic) return src.videoKey;
5374
+ return null;
5375
+ }
5376
+
5281
5377
  // src/digest/style.ts
5282
5378
  var STYLE_FIELDS = [
5283
5379
  "zoomStyle",
@@ -5297,14 +5393,116 @@ function pickStyle(doc) {
5297
5393
  }
5298
5394
  return out;
5299
5395
  }
5300
- function copyStyle(from, to) {
5396
+ function copyStyle(from, to, opts = {}) {
5301
5397
  const next = structuredClone(to);
5302
5398
  const style = pickStyle(from);
5303
5399
  for (const k of STYLE_FIELDS) {
5304
5400
  if (k in style) next[k] = style[k];
5305
5401
  else delete next[k];
5306
5402
  }
5307
- return next;
5403
+ const styled = next;
5404
+ return opts.layout ? copyLayout(from, styled, { keys: opts.keys }).doc : styled;
5405
+ }
5406
+ var LAYOUT_FRAME_FIELDS = [
5407
+ "background",
5408
+ "padding",
5409
+ "radius",
5410
+ "shadow",
5411
+ "shadowContact",
5412
+ "shadowColor",
5413
+ "inset",
5414
+ "border",
5415
+ "borderWidth",
5416
+ "borderColor",
5417
+ "fit",
5418
+ "focus",
5419
+ "focusFollow",
5420
+ "parallax",
5421
+ "entrance"
5422
+ ];
5423
+ var STAGE_CLIP_PREFIX = "stage-";
5424
+ var REST_TILT_ID = "rest";
5425
+ var isStageClip = (clip) => clip.id.startsWith(STAGE_CLIP_PREFIX);
5426
+ function restLeanOf(doc) {
5427
+ const spans = doc.tilt ?? [];
5428
+ const named = spans.find((z) => z.id === REST_TILT_ID || z.id === "stage");
5429
+ if (named) return { rx: named.rx, ry: named.ry };
5430
+ const total = anchorSourceDuration(doc);
5431
+ const whole = spans.find((z) => z.out - z.in >= total * 0.9);
5432
+ return whole ? { rx: whole.rx, ry: whole.ry } : null;
5433
+ }
5434
+ function layoutOf(doc) {
5435
+ const frame = doc.frame;
5436
+ return {
5437
+ frame: LAYOUT_FRAME_FIELDS.filter((k) => frame[k] !== void 0),
5438
+ clips: (doc.overlays ?? []).filter(isStageClip).map((o) => o.id),
5439
+ lean: restLeanOf(doc) !== null,
5440
+ hold: (doc.segments.at(-1)?.hold ?? 0) > 0
5441
+ };
5442
+ }
5443
+ function copyLayout(from, to, opts = {}) {
5444
+ const doc = structuredClone(to);
5445
+ const notes = [];
5446
+ const frame = doc.frame;
5447
+ const src = from.frame;
5448
+ for (const k of LAYOUT_FRAME_FIELDS) {
5449
+ if (src[k] !== void 0) frame[k] = structuredClone(src[k]);
5450
+ else delete frame[k];
5451
+ }
5452
+ if (from.frame.backgroundMedia)
5453
+ doc.frame.backgroundMedia = structuredClone(from.frame.backgroundMedia);
5454
+ const outDur = docOutputDuration(doc);
5455
+ const own = new Map((doc.overlays ?? []).map((o) => [o.id, o]));
5456
+ const merged = (doc.overlays ?? []).filter(
5457
+ (o) => !isStageClip(o)
5458
+ );
5459
+ const skippedMarks = [];
5460
+ for (const clip of (from.overlays ?? []).filter(isStageClip)) {
5461
+ const mine = own.get(clip.id);
5462
+ const next = structuredClone(clip);
5463
+ next.start = 0;
5464
+ next.duration = Math.max(0.3, Math.round(outDur * 1e3) / 1e3);
5465
+ if (next.kind === "text") {
5466
+ if (mine?.kind === "text") next.text = mine.text;
5467
+ } else if (opts.keys?.[clip.id]) {
5468
+ next.key = opts.keys[clip.id];
5469
+ } else if (mine && mine.kind !== "text") {
5470
+ next.key = mine.key;
5471
+ } else {
5472
+ skippedMarks.push(clip.id);
5473
+ continue;
5474
+ }
5475
+ merged.push(next);
5476
+ }
5477
+ for (const o of doc.overlays ?? []) {
5478
+ if (isStageClip(o) && !merged.some((m) => m.id === o.id)) merged.push(o);
5479
+ }
5480
+ doc.overlays = merged.length ? merged : void 0;
5481
+ if (skippedMarks.length)
5482
+ notes.push(
5483
+ `${skippedMarks.join(", ")} left out: the layout's image is its own brand's; add yours`
5484
+ );
5485
+ const lean = restLeanOf(from);
5486
+ if (lean) {
5487
+ if (!doc.tilt?.length) {
5488
+ doc.tilt = [
5489
+ {
5490
+ id: REST_TILT_ID,
5491
+ in: 0,
5492
+ out: Math.round(anchorSourceDuration(doc) * 1e3) / 1e3,
5493
+ rx: lean.rx,
5494
+ ry: lean.ry,
5495
+ source: "manual"
5496
+ }
5497
+ ];
5498
+ } else {
5499
+ notes.push("lean kept: this take has tilt spans of its own");
5500
+ }
5501
+ }
5502
+ const hold = from.segments.at(-1)?.hold;
5503
+ const last = doc.segments.at(-1);
5504
+ if (typeof hold === "number" && hold > 0 && last) last.hold = hold;
5505
+ return { doc, notes };
5308
5506
  }
5309
5507
 
5310
5508
  // src/digest/geometry.ts
@@ -5382,7 +5580,7 @@ function cropBox(m, geo, meta) {
5382
5580
  }
5383
5581
 
5384
5582
  // src/digest/build.ts
5385
- import { totalDuration as totalDuration2 } from "@vosjs/timeline";
5583
+ import { totalDuration as totalDuration4 } from "@vosjs/timeline";
5386
5584
  var DIGEST_VERSION = 1;
5387
5585
  function buildDigest(input) {
5388
5586
  const { doc, meta } = { doc: input.doc, meta: input.doc.source.meta };
@@ -5450,7 +5648,7 @@ function buildDigest(input) {
5450
5648
  };
5451
5649
  }
5452
5650
  function outputDurationOf(doc) {
5453
- return totalDuration2(ratedSegments(doc));
5651
+ return totalDuration4(ratedSegments(doc));
5454
5652
  }
5455
5653
  function round8(v) {
5456
5654
  return Math.round(v * 1e3) / 1e3;
@@ -5548,7 +5746,7 @@ function round9(v) {
5548
5746
 
5549
5747
  // src/lower/lowerStudioDoc.ts
5550
5748
  import { applyTimelineEdits } from "@vosjs/shared/timelineEdits";
5551
- import { totalDuration as totalDuration3 } from "@vosjs/timeline";
5749
+ import { totalDuration as totalDuration5 } from "@vosjs/timeline";
5552
5750
  function lowerStudioDoc(doc, opts = {}) {
5553
5751
  return isRecordingDoc(doc) ? lowerToComposition(doc) : lowerProgramDoc(doc, opts);
5554
5752
  }
@@ -5579,7 +5777,7 @@ function lowerProgramDoc(doc, opts = {}) {
5579
5777
  ) : doc.program.config;
5580
5778
  const own = programDuration(doc);
5581
5779
  const rated = ratedSegments(doc);
5582
- const duration = own > 0 ? totalDuration3(rated) : 0;
5780
+ const duration = own > 0 ? totalDuration5(rated) : 0;
5583
5781
  const entryData = studioLayerData(doc, duration);
5584
5782
  const baseData = anchor.data && typeof anchor.data === "object" ? anchor.data : {};
5585
5783
  const retimed = !!doc.speed?.length && own > 0;
@@ -6177,99 +6375,15 @@ import {
6177
6375
  removeSegment,
6178
6376
  segmentRate as segmentRate3,
6179
6377
  sourceToTimeline as sourceToTimeline3,
6180
- splitBySpeed as splitBySpeed2,
6181
- totalDuration as totalDuration5,
6378
+ splitBySpeed as splitBySpeed3,
6379
+ totalDuration as totalDuration6,
6182
6380
  trimSegment
6183
6381
  } from "@vosjs/timeline";
6184
-
6185
- // src/audioBeds.ts
6186
- import { totalDuration as totalDuration4 } from "@vosjs/timeline";
6187
- var round32 = (v) => Math.round(v * 1e3) / 1e3;
6188
- function docOutputDuration(doc) {
6189
- return totalDuration4(ratedSegments(doc));
6190
- }
6191
- function musicBedClip(input) {
6192
- const track = round32(input.trackDuration);
6193
- const output = round32(input.outputDuration);
6194
- const clip = {
6195
- id: input.id,
6196
- key: input.key,
6197
- name: input.name,
6198
- start: 0,
6199
- in: 0,
6200
- out: track,
6201
- duration: track,
6202
- // Under speech, not over it; catalog tracks are loudness-normalized so
6203
- // one default means the same thing across the library.
6204
- gain: 0.5,
6205
- fadeIn: 0,
6206
- // A bed that just stops reads as a glitch; clamp so a very short cut
6207
- // still spends most of its time at full level.
6208
- fadeOut: output > 0 ? Math.min(1.5, round32(output / 4)) : 1.5,
6209
- duck: input.hasMic || void 0
6210
- };
6211
- if (output <= 0) return clip;
6212
- if (track >= output) {
6213
- clip.out = output;
6214
- } else {
6215
- clip.loop = true;
6216
- clip.loopLen = output;
6217
- }
6218
- return clip;
6219
- }
6220
- function isMusicBed(clip, outputDuration) {
6221
- if (clip.start > 0.05) return false;
6222
- if (clip.loop) return true;
6223
- const placed = clip.out - clip.in;
6224
- return Math.abs(placed - outputDuration) <= 1;
6225
- }
6226
- function refillAudioBeds(doc, prevDuration, nextDuration) {
6227
- const EPS2 = 0.05;
6228
- if (Math.abs(nextDuration - prevDuration) <= EPS2 || nextDuration <= 0) {
6229
- return false;
6230
- }
6231
- let changed = false;
6232
- for (const clip of doc.audio) {
6233
- if (clip.start >= nextDuration) continue;
6234
- const placed = clip.loop ? Math.max(clip.out - clip.in, clip.loopLen ?? clip.out - clip.in) : clip.out - clip.in;
6235
- if (Math.abs(clip.start + placed - prevDuration) > EPS2) continue;
6236
- const span = clip.out - clip.in;
6237
- const nextLen = round32(nextDuration - clip.start);
6238
- if (clip.loop) {
6239
- if (nextLen >= span) {
6240
- clip.loopLen = nextLen;
6241
- } else {
6242
- clip.loop = void 0;
6243
- clip.loopLen = void 0;
6244
- clip.out = round32(clip.in + nextLen);
6245
- }
6246
- } else {
6247
- if (clip.in + nextLen <= clip.duration) {
6248
- clip.out = round32(clip.in + nextLen);
6249
- } else {
6250
- clip.out = clip.duration;
6251
- clip.loop = true;
6252
- clip.loopLen = nextLen;
6253
- }
6254
- }
6255
- changed = true;
6256
- }
6257
- return changed;
6258
- }
6259
- function voiceKey(doc) {
6260
- if (!("source" in doc)) return null;
6261
- const src = doc.source;
6262
- if (src.micKey) return src.micKey;
6263
- if (src.meta.hasAudio && !src.meta.hasMic) return src.videoKey;
6264
- return null;
6265
- }
6266
-
6267
- // src/timeline/lanes.ts
6268
6382
  function effectiveSegments(doc) {
6269
6383
  if (isRecordingDoc(doc) && doc.segments.length) return doc.segments;
6270
6384
  return [{ in: 0, out: anchorSourceDuration(doc) }];
6271
6385
  }
6272
- var outputLen = (seg, speeds) => totalDuration5(splitBySpeed2([seg], speeds));
6386
+ var outputLen = (seg, speeds) => totalDuration6(splitBySpeed3([seg], speeds));
6273
6387
  var segmentStarts = (segments, speeds) => {
6274
6388
  const starts = [];
6275
6389
  let acc = 0;
@@ -6326,7 +6440,7 @@ var videoLane = {
6326
6440
  if (index < 0 || index >= segments.length) return null;
6327
6441
  const seg = segments[index];
6328
6442
  const starts = segmentStarts(segments, speeds);
6329
- const fullMap = splitBySpeed2([{ in: 0, out: sourceDuration }], speeds);
6443
+ const fullMap = splitBySpeed3([{ in: 0, out: sourceDuration }], speeds);
6330
6444
  const edgeSrc = g.edge === "start" ? seg.in : seg.out;
6331
6445
  const edgeOutNow = g.edge === "start" ? starts[index] : starts[index] + outputLen(seg, speeds);
6332
6446
  const anchorOut = sourceToTimeline3(fullMap, Math.min(edgeSrc, sourceDuration)) ?? edgeSrc;
@@ -6349,7 +6463,7 @@ var videoLane = {
6349
6463
  );
6350
6464
  if (index < 0) return null;
6351
6465
  const s = segments[index];
6352
- const sourceT = mapTime2(splitBySpeed2([s], speeds), g.t - starts[index]);
6466
+ const sourceT = mapTime2(splitBySpeed3([s], speeds), g.t - starts[index]);
6353
6467
  if (sourceT - s.in < 0.05 || s.out - sourceT < 0.05) return null;
6354
6468
  const next = [
6355
6469
  ...segments.slice(0, index),
@@ -6901,7 +7015,7 @@ var speedLane = {
6901
7015
  const sp = spans.find((s) => s.id === g.id);
6902
7016
  if (!sp) return null;
6903
7017
  const others = spans.filter((o) => o.id !== g.id);
6904
- const base = splitBySpeed2(effectiveSegments(doc), others);
7018
+ const base = splitBySpeed3(effectiveSegments(doc), others);
6905
7019
  if (g.type === "move") {
6906
7020
  const len = sp.out - sp.in;
6907
7021
  let newIn = clampToKept(
@@ -7475,6 +7589,7 @@ export {
7475
7589
  FRAME_BORDER_DEFAULT,
7476
7590
  FRAME_BORDER_WIDTH_DEFAULT,
7477
7591
  HOUSE_GRADIENT,
7592
+ LAYOUT_FRAME_FIELDS,
7478
7593
  LOOK_KINDS,
7479
7594
  MINIMAL_BAR_THEMES,
7480
7595
  MOTION_DELTA,
@@ -7494,11 +7609,13 @@ export {
7494
7609
  PLAYBACK_ACTIVITY,
7495
7610
  PROGRAM_RETIME,
7496
7611
  REJECT_OVERLAP,
7612
+ REST_TILT_ID,
7497
7613
  SCENE_MOTION,
7498
7614
  SCENE_QUIET,
7499
7615
  SPEED_RATE_MAX,
7500
7616
  SPEED_RATE_MIN,
7501
7617
  SPEED_SPAN_MIN,
7618
+ STAGE_CLIP_PREFIX,
7502
7619
  STUDIO_ENTRY_ID,
7503
7620
  STYLE_FIELDS,
7504
7621
  TEXT3D_DEPTH_DEFAULT,
@@ -7560,6 +7677,7 @@ export {
7560
7677
  computeCardLayout,
7561
7678
  computeMicRms,
7562
7679
  computePeaks,
7680
+ copyLayout,
7563
7681
  copyStyle,
7564
7682
  cropBox,
7565
7683
  cursorIdleFade,
@@ -7568,6 +7686,7 @@ export {
7568
7686
  destinationsForChannel,
7569
7687
  docCardLayout,
7570
7688
  docOutputDuration,
7689
+ docRestTime,
7571
7690
  docToCropSpace,
7572
7691
  docToFullSpace,
7573
7692
  duckCurve,
@@ -7591,6 +7710,8 @@ export {
7591
7710
  isProgramDoc,
7592
7711
  isRecordingDoc,
7593
7712
  isRejected,
7713
+ isStageClip,
7714
+ layoutOf,
7594
7715
  levelForFocusFraction,
7595
7716
  lookFromBrand,
7596
7717
  lookKindForGround,
@@ -7640,6 +7761,7 @@ export {
7640
7761
  resolveOverlayStyle,
7641
7762
  resolveText3dAsset,
7642
7763
  resolveZoomStyle,
7764
+ restLeanOf,
7643
7765
  sceneChanges,
7644
7766
  scrollRuns,
7645
7767
  setSpeedInRange,