mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-25 23:35:43 +00:00
feat: persist caption and editor control state
Persist the new caption, cursor, and zoom timing controls in editor preferences and project files. Normalize those saved values so older projects continue loading with stable defaults.
This commit is contained in:
@@ -78,8 +78,17 @@ describe("editorPreferences", () => {
|
||||
backgroundBlur: 3.5,
|
||||
zoomMotionBlur: DEFAULT_EDITOR_PREFERENCES.zoomMotionBlur,
|
||||
connectZooms: DEFAULT_EDITOR_PREFERENCES.connectZooms,
|
||||
zoomInDurationMs: DEFAULT_EDITOR_PREFERENCES.zoomInDurationMs,
|
||||
zoomInOverlapMs: DEFAULT_EDITOR_PREFERENCES.zoomInOverlapMs,
|
||||
zoomOutDurationMs: DEFAULT_EDITOR_PREFERENCES.zoomOutDurationMs,
|
||||
connectedZoomGapMs: DEFAULT_EDITOR_PREFERENCES.connectedZoomGapMs,
|
||||
connectedZoomDurationMs: DEFAULT_EDITOR_PREFERENCES.connectedZoomDurationMs,
|
||||
zoomInEasing: DEFAULT_EDITOR_PREFERENCES.zoomInEasing,
|
||||
zoomOutEasing: DEFAULT_EDITOR_PREFERENCES.zoomOutEasing,
|
||||
connectedZoomEasing: DEFAULT_EDITOR_PREFERENCES.connectedZoomEasing,
|
||||
showCursor: false,
|
||||
loopCursor: DEFAULT_EDITOR_PREFERENCES.loopCursor,
|
||||
cursorStyle: DEFAULT_EDITOR_PREFERENCES.cursorStyle,
|
||||
cursorSize: DEFAULT_EDITOR_PREFERENCES.cursorSize,
|
||||
cursorSmoothing: DEFAULT_EDITOR_PREFERENCES.cursorSmoothing,
|
||||
cursorMotionBlur: DEFAULT_EDITOR_PREFERENCES.cursorMotionBlur,
|
||||
@@ -98,6 +107,8 @@ describe("editorPreferences", () => {
|
||||
customAspectWidth: "21",
|
||||
customAspectHeight: "9",
|
||||
customWallpapers: ["data:image/jpeg;base64,abc"],
|
||||
whisperExecutablePath: DEFAULT_EDITOR_PREFERENCES.whisperExecutablePath,
|
||||
whisperModelPath: DEFAULT_EDITOR_PREFERENCES.whisperModelPath,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -133,8 +144,17 @@ describe("editorPreferences", () => {
|
||||
backgroundBlur: DEFAULT_EDITOR_PREFERENCES.backgroundBlur,
|
||||
zoomMotionBlur: DEFAULT_EDITOR_PREFERENCES.zoomMotionBlur,
|
||||
connectZooms: DEFAULT_EDITOR_PREFERENCES.connectZooms,
|
||||
zoomInDurationMs: DEFAULT_EDITOR_PREFERENCES.zoomInDurationMs,
|
||||
zoomInOverlapMs: DEFAULT_EDITOR_PREFERENCES.zoomInOverlapMs,
|
||||
zoomOutDurationMs: DEFAULT_EDITOR_PREFERENCES.zoomOutDurationMs,
|
||||
connectedZoomGapMs: DEFAULT_EDITOR_PREFERENCES.connectedZoomGapMs,
|
||||
connectedZoomDurationMs: DEFAULT_EDITOR_PREFERENCES.connectedZoomDurationMs,
|
||||
zoomInEasing: DEFAULT_EDITOR_PREFERENCES.zoomInEasing,
|
||||
zoomOutEasing: DEFAULT_EDITOR_PREFERENCES.zoomOutEasing,
|
||||
connectedZoomEasing: DEFAULT_EDITOR_PREFERENCES.connectedZoomEasing,
|
||||
showCursor: DEFAULT_EDITOR_PREFERENCES.showCursor,
|
||||
loopCursor: DEFAULT_EDITOR_PREFERENCES.loopCursor,
|
||||
cursorStyle: DEFAULT_EDITOR_PREFERENCES.cursorStyle,
|
||||
cursorSize: DEFAULT_EDITOR_PREFERENCES.cursorSize,
|
||||
cursorSmoothing: DEFAULT_EDITOR_PREFERENCES.cursorSmoothing,
|
||||
cursorMotionBlur: DEFAULT_EDITOR_PREFERENCES.cursorMotionBlur,
|
||||
@@ -152,6 +172,8 @@ describe("editorPreferences", () => {
|
||||
customAspectWidth: "21",
|
||||
customAspectHeight: "9",
|
||||
customWallpapers: DEFAULT_EDITOR_PREFERENCES.customWallpapers,
|
||||
whisperExecutablePath: DEFAULT_EDITOR_PREFERENCES.whisperExecutablePath,
|
||||
whisperModelPath: DEFAULT_EDITOR_PREFERENCES.whisperModelPath,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -165,8 +187,17 @@ describe("editorPreferences", () => {
|
||||
backgroundBlur: 1.5,
|
||||
zoomMotionBlur: 0.75,
|
||||
connectZooms: false,
|
||||
zoomInDurationMs: DEFAULT_EDITOR_PREFERENCES.zoomInDurationMs,
|
||||
zoomInOverlapMs: DEFAULT_EDITOR_PREFERENCES.zoomInOverlapMs,
|
||||
zoomOutDurationMs: DEFAULT_EDITOR_PREFERENCES.zoomOutDurationMs,
|
||||
connectedZoomGapMs: DEFAULT_EDITOR_PREFERENCES.connectedZoomGapMs,
|
||||
connectedZoomDurationMs: DEFAULT_EDITOR_PREFERENCES.connectedZoomDurationMs,
|
||||
zoomInEasing: DEFAULT_EDITOR_PREFERENCES.zoomInEasing,
|
||||
zoomOutEasing: DEFAULT_EDITOR_PREFERENCES.zoomOutEasing,
|
||||
connectedZoomEasing: DEFAULT_EDITOR_PREFERENCES.connectedZoomEasing,
|
||||
showCursor: false,
|
||||
loopCursor: true,
|
||||
cursorStyle: "figma",
|
||||
cursorSize: 3,
|
||||
cursorSmoothing: 1.25,
|
||||
cursorMotionBlur: 0.5,
|
||||
@@ -192,8 +223,17 @@ describe("editorPreferences", () => {
|
||||
backgroundBlur: 1.5,
|
||||
zoomMotionBlur: 0.75,
|
||||
connectZooms: false,
|
||||
zoomInDurationMs: DEFAULT_EDITOR_PREFERENCES.zoomInDurationMs,
|
||||
zoomInOverlapMs: DEFAULT_EDITOR_PREFERENCES.zoomInOverlapMs,
|
||||
zoomOutDurationMs: DEFAULT_EDITOR_PREFERENCES.zoomOutDurationMs,
|
||||
connectedZoomGapMs: DEFAULT_EDITOR_PREFERENCES.connectedZoomGapMs,
|
||||
connectedZoomDurationMs: DEFAULT_EDITOR_PREFERENCES.connectedZoomDurationMs,
|
||||
zoomInEasing: DEFAULT_EDITOR_PREFERENCES.zoomInEasing,
|
||||
zoomOutEasing: DEFAULT_EDITOR_PREFERENCES.zoomOutEasing,
|
||||
connectedZoomEasing: DEFAULT_EDITOR_PREFERENCES.connectedZoomEasing,
|
||||
showCursor: false,
|
||||
loopCursor: true,
|
||||
cursorStyle: "figma",
|
||||
cursorSize: 3,
|
||||
cursorSmoothing: 1.25,
|
||||
cursorMotionBlur: 0.5,
|
||||
@@ -212,6 +252,8 @@ describe("editorPreferences", () => {
|
||||
customAspectWidth: "4",
|
||||
customAspectHeight: "5",
|
||||
customWallpapers: ["data:image/jpeg;base64,abc"],
|
||||
whisperExecutablePath: DEFAULT_EDITOR_PREFERENCES.whisperExecutablePath,
|
||||
whisperModelPath: DEFAULT_EDITOR_PREFERENCES.whisperModelPath,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,8 +7,17 @@ type PersistedEditorControls = Pick<
|
||||
| "backgroundBlur"
|
||||
| "zoomMotionBlur"
|
||||
| "connectZooms"
|
||||
| "zoomInDurationMs"
|
||||
| "zoomInOverlapMs"
|
||||
| "zoomOutDurationMs"
|
||||
| "connectedZoomGapMs"
|
||||
| "connectedZoomDurationMs"
|
||||
| "zoomInEasing"
|
||||
| "zoomOutEasing"
|
||||
| "connectedZoomEasing"
|
||||
| "showCursor"
|
||||
| "loopCursor"
|
||||
| "cursorStyle"
|
||||
| "cursorSize"
|
||||
| "cursorSmoothing"
|
||||
| "cursorMotionBlur"
|
||||
@@ -32,6 +41,8 @@ export interface EditorPreferences extends PersistedEditorControls {
|
||||
customAspectWidth: string;
|
||||
customAspectHeight: string;
|
||||
customWallpapers: string[];
|
||||
whisperExecutablePath: string | null;
|
||||
whisperModelPath: string | null;
|
||||
}
|
||||
|
||||
export const EDITOR_PREFERENCES_STORAGE_KEY = "recordly.editor.preferences";
|
||||
@@ -44,8 +55,17 @@ export const DEFAULT_EDITOR_PREFERENCES: EditorPreferences = {
|
||||
backgroundBlur: DEFAULT_EDITOR_CONTROLS.backgroundBlur,
|
||||
zoomMotionBlur: DEFAULT_EDITOR_CONTROLS.zoomMotionBlur,
|
||||
connectZooms: DEFAULT_EDITOR_CONTROLS.connectZooms,
|
||||
zoomInDurationMs: DEFAULT_EDITOR_CONTROLS.zoomInDurationMs,
|
||||
zoomInOverlapMs: DEFAULT_EDITOR_CONTROLS.zoomInOverlapMs,
|
||||
zoomOutDurationMs: DEFAULT_EDITOR_CONTROLS.zoomOutDurationMs,
|
||||
connectedZoomGapMs: DEFAULT_EDITOR_CONTROLS.connectedZoomGapMs,
|
||||
connectedZoomDurationMs: DEFAULT_EDITOR_CONTROLS.connectedZoomDurationMs,
|
||||
zoomInEasing: DEFAULT_EDITOR_CONTROLS.zoomInEasing,
|
||||
zoomOutEasing: DEFAULT_EDITOR_CONTROLS.zoomOutEasing,
|
||||
connectedZoomEasing: DEFAULT_EDITOR_CONTROLS.connectedZoomEasing,
|
||||
showCursor: DEFAULT_EDITOR_CONTROLS.showCursor,
|
||||
loopCursor: DEFAULT_EDITOR_CONTROLS.loopCursor,
|
||||
cursorStyle: DEFAULT_EDITOR_CONTROLS.cursorStyle,
|
||||
cursorSize: DEFAULT_EDITOR_CONTROLS.cursorSize,
|
||||
cursorSmoothing: DEFAULT_EDITOR_CONTROLS.cursorSmoothing,
|
||||
cursorMotionBlur: DEFAULT_EDITOR_CONTROLS.cursorMotionBlur,
|
||||
@@ -64,6 +84,8 @@ export const DEFAULT_EDITOR_PREFERENCES: EditorPreferences = {
|
||||
customAspectWidth: "16",
|
||||
customAspectHeight: "9",
|
||||
customWallpapers: [],
|
||||
whisperExecutablePath: null,
|
||||
whisperModelPath: null,
|
||||
};
|
||||
|
||||
function normalizePositiveIntegerString(value: unknown, fallback: string): string {
|
||||
@@ -89,6 +111,15 @@ function normalizeCustomWallpapers(value: unknown, fallback: string[]): string[]
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeNullablePath(value: unknown): string | null {
|
||||
if (typeof value !== "string") {
|
||||
return null;
|
||||
}
|
||||
|
||||
const trimmed = value.trim();
|
||||
return trimmed.length > 0 ? trimmed : null;
|
||||
}
|
||||
|
||||
function normalizeEditorControls(
|
||||
raw: Partial<EditorPreferences>,
|
||||
fallback: EditorPreferences,
|
||||
@@ -99,8 +130,19 @@ function normalizeEditorControls(
|
||||
backgroundBlur: raw.backgroundBlur ?? fallback.backgroundBlur,
|
||||
zoomMotionBlur: raw.zoomMotionBlur ?? fallback.zoomMotionBlur,
|
||||
connectZooms: raw.connectZooms ?? fallback.connectZooms,
|
||||
zoomInDurationMs: raw.zoomInDurationMs ?? fallback.zoomInDurationMs,
|
||||
zoomInOverlapMs: raw.zoomInOverlapMs ?? fallback.zoomInOverlapMs,
|
||||
zoomOutDurationMs: raw.zoomOutDurationMs ?? fallback.zoomOutDurationMs,
|
||||
connectedZoomGapMs: raw.connectedZoomGapMs ?? fallback.connectedZoomGapMs,
|
||||
connectedZoomDurationMs:
|
||||
raw.connectedZoomDurationMs ?? fallback.connectedZoomDurationMs,
|
||||
zoomInEasing: raw.zoomInEasing ?? fallback.zoomInEasing,
|
||||
zoomOutEasing: raw.zoomOutEasing ?? fallback.zoomOutEasing,
|
||||
connectedZoomEasing:
|
||||
raw.connectedZoomEasing ?? fallback.connectedZoomEasing,
|
||||
showCursor: raw.showCursor ?? fallback.showCursor,
|
||||
loopCursor: raw.loopCursor ?? fallback.loopCursor,
|
||||
cursorStyle: raw.cursorStyle ?? fallback.cursorStyle,
|
||||
cursorSize: raw.cursorSize ?? fallback.cursorSize,
|
||||
cursorSmoothing: raw.cursorSmoothing ?? fallback.cursorSmoothing,
|
||||
cursorMotionBlur: raw.cursorMotionBlur ?? fallback.cursorMotionBlur,
|
||||
@@ -127,8 +169,17 @@ function normalizeEditorControls(
|
||||
backgroundBlur: normalized.backgroundBlur,
|
||||
zoomMotionBlur: normalized.zoomMotionBlur,
|
||||
connectZooms: normalized.connectZooms,
|
||||
zoomInDurationMs: normalized.zoomInDurationMs,
|
||||
zoomInOverlapMs: normalized.zoomInOverlapMs,
|
||||
zoomOutDurationMs: normalized.zoomOutDurationMs,
|
||||
connectedZoomGapMs: normalized.connectedZoomGapMs,
|
||||
connectedZoomDurationMs: normalized.connectedZoomDurationMs,
|
||||
zoomInEasing: normalized.zoomInEasing,
|
||||
zoomOutEasing: normalized.zoomOutEasing,
|
||||
connectedZoomEasing: normalized.connectedZoomEasing,
|
||||
showCursor: normalized.showCursor,
|
||||
loopCursor: normalized.loopCursor,
|
||||
cursorStyle: normalized.cursorStyle,
|
||||
cursorSize: normalized.cursorSize,
|
||||
cursorSmoothing: normalized.cursorSmoothing,
|
||||
cursorMotionBlur: normalized.cursorMotionBlur,
|
||||
@@ -165,6 +216,10 @@ export function normalizeEditorPreferences(
|
||||
fallback.customAspectHeight,
|
||||
),
|
||||
customWallpapers: normalizeCustomWallpapers(raw.customWallpapers, fallback.customWallpapers),
|
||||
whisperExecutablePath:
|
||||
normalizeNullablePath(raw.whisperExecutablePath) ?? fallback.whisperExecutablePath,
|
||||
whisperModelPath:
|
||||
normalizeNullablePath(raw.whisperModelPath) ?? fallback.whisperModelPath,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,16 @@ import type { ExportFormat, ExportQuality, GifFrameRate, GifSizePreset } from "@
|
||||
import { DEFAULT_WALLPAPER_PATH } from "@/lib/wallpapers";
|
||||
import { ASPECT_RATIOS, type AspectRatio, isCustomAspectRatio } from "@/utils/aspectRatioUtils";
|
||||
import {
|
||||
type AutoCaptionAnimation,
|
||||
type AutoCaptionSettings,
|
||||
type CaptionCue,
|
||||
type CaptionCueWord,
|
||||
type AnnotationRegion,
|
||||
type AudioRegion,
|
||||
type CropRegion,
|
||||
type CursorStyle,
|
||||
DEFAULT_AUTO_CAPTION_SETTINGS,
|
||||
getDefaultCaptionFontFamily,
|
||||
DEFAULT_ANNOTATION_POSITION,
|
||||
DEFAULT_ANNOTATION_SIZE,
|
||||
DEFAULT_ANNOTATION_STYLE,
|
||||
@@ -13,8 +20,12 @@ import {
|
||||
DEFAULT_CURSOR_CLICK_BOUNCE_DURATION,
|
||||
DEFAULT_CURSOR_MOTION_BLUR,
|
||||
DEFAULT_CURSOR_SIZE,
|
||||
DEFAULT_CURSOR_STYLE,
|
||||
DEFAULT_CURSOR_SMOOTHING,
|
||||
DEFAULT_CURSOR_SWAY,
|
||||
DEFAULT_CONNECTED_ZOOM_DURATION_MS,
|
||||
DEFAULT_CONNECTED_ZOOM_EASING,
|
||||
DEFAULT_CONNECTED_ZOOM_GAP_MS,
|
||||
DEFAULT_WEBCAM_CORNER_RADIUS,
|
||||
DEFAULT_WEBCAM_MARGIN,
|
||||
DEFAULT_WEBCAM_OVERLAY,
|
||||
@@ -27,10 +38,16 @@ import {
|
||||
DEFAULT_FIGURE_DATA,
|
||||
DEFAULT_PLAYBACK_SPEED,
|
||||
DEFAULT_ZOOM_DEPTH,
|
||||
DEFAULT_ZOOM_IN_DURATION_MS,
|
||||
DEFAULT_ZOOM_IN_EASING,
|
||||
DEFAULT_ZOOM_IN_OVERLAP_MS,
|
||||
DEFAULT_ZOOM_MOTION_BLUR,
|
||||
DEFAULT_ZOOM_OUT_DURATION_MS,
|
||||
DEFAULT_ZOOM_OUT_EASING,
|
||||
type SpeedRegion,
|
||||
type TrimRegion,
|
||||
type WebcamOverlaySettings,
|
||||
type ZoomTransitionEasing,
|
||||
type ZoomRegion,
|
||||
} from "./types";
|
||||
|
||||
@@ -42,8 +59,17 @@ export interface ProjectEditorState {
|
||||
backgroundBlur: number;
|
||||
zoomMotionBlur: number;
|
||||
connectZooms: boolean;
|
||||
zoomInDurationMs: number;
|
||||
zoomInOverlapMs: number;
|
||||
zoomOutDurationMs: number;
|
||||
connectedZoomGapMs: number;
|
||||
connectedZoomDurationMs: number;
|
||||
zoomInEasing: ZoomTransitionEasing;
|
||||
zoomOutEasing: ZoomTransitionEasing;
|
||||
connectedZoomEasing: ZoomTransitionEasing;
|
||||
showCursor: boolean;
|
||||
loopCursor: boolean;
|
||||
cursorStyle: CursorStyle;
|
||||
cursorSize: number;
|
||||
cursorSmoothing: number;
|
||||
cursorMotionBlur: number;
|
||||
@@ -58,6 +84,8 @@ export interface ProjectEditorState {
|
||||
speedRegions: SpeedRegion[];
|
||||
annotationRegions: AnnotationRegion[];
|
||||
audioRegions: AudioRegion[];
|
||||
autoCaptions: CaptionCue[];
|
||||
autoCaptionSettings: AutoCaptionSettings;
|
||||
webcam: WebcamOverlaySettings;
|
||||
aspectRatio: AspectRatio;
|
||||
exportQuality: ExportQuality;
|
||||
@@ -81,6 +109,28 @@ function clamp(value: number, min: number, max: number) {
|
||||
return Math.min(max, Math.max(min, value));
|
||||
}
|
||||
|
||||
function normalizeZoomTransitionEasing(
|
||||
value: unknown,
|
||||
fallback: ZoomTransitionEasing,
|
||||
): ZoomTransitionEasing {
|
||||
return value === "recordly" ||
|
||||
value === "glide" ||
|
||||
value === "smooth" ||
|
||||
value === "snappy" ||
|
||||
value === "linear"
|
||||
? value
|
||||
: fallback;
|
||||
}
|
||||
|
||||
function normalizeAutoCaptionAnimation(
|
||||
value: unknown,
|
||||
fallback: AutoCaptionAnimation,
|
||||
): AutoCaptionAnimation {
|
||||
return value === "none" || value === "fade" || value === "rise" || value === "pop"
|
||||
? value
|
||||
: fallback;
|
||||
}
|
||||
|
||||
function isFileUrl(value: string): boolean {
|
||||
return /^file:\/\//i.test(value);
|
||||
}
|
||||
@@ -187,6 +237,21 @@ export function normalizeProjectEditor(editor: Partial<ProjectEditorState>): Pro
|
||||
: legacyShowBlur
|
||||
? 2
|
||||
: 0;
|
||||
const normalizedZoomInDurationMs = isFiniteNumber(editor.zoomInDurationMs)
|
||||
? clamp(editor.zoomInDurationMs, 60, 4000)
|
||||
: DEFAULT_ZOOM_IN_DURATION_MS;
|
||||
const normalizedZoomInOverlapMs = isFiniteNumber(editor.zoomInOverlapMs)
|
||||
? clamp(editor.zoomInOverlapMs, 0, normalizedZoomInDurationMs)
|
||||
: DEFAULT_ZOOM_IN_OVERLAP_MS;
|
||||
const normalizedZoomOutDurationMs = isFiniteNumber(editor.zoomOutDurationMs)
|
||||
? clamp(editor.zoomOutDurationMs, 60, 4000)
|
||||
: DEFAULT_ZOOM_OUT_DURATION_MS;
|
||||
const normalizedConnectedZoomGapMs = isFiniteNumber(editor.connectedZoomGapMs)
|
||||
? clamp(editor.connectedZoomGapMs, 0, 5000)
|
||||
: DEFAULT_CONNECTED_ZOOM_GAP_MS;
|
||||
const normalizedConnectedZoomDurationMs = isFiniteNumber(editor.connectedZoomDurationMs)
|
||||
? clamp(editor.connectedZoomDurationMs, 60, 4000)
|
||||
: DEFAULT_CONNECTED_ZOOM_DURATION_MS;
|
||||
|
||||
const normalizedZoomRegions: ZoomRegion[] = Array.isArray(editor.zoomRegions)
|
||||
? editor.zoomRegions
|
||||
@@ -342,6 +407,95 @@ export function normalizeProjectEditor(editor: Partial<ProjectEditorState>): Pro
|
||||
})
|
||||
: [];
|
||||
|
||||
const normalizedAutoCaptions: CaptionCue[] = Array.isArray(
|
||||
(editor as Partial<ProjectEditorState>).autoCaptions,
|
||||
)
|
||||
? ((editor as Partial<ProjectEditorState>).autoCaptions as CaptionCue[])
|
||||
.filter((cue): cue is CaptionCue => Boolean(cue && typeof cue.id === "string"))
|
||||
.map((cue) => {
|
||||
const rawStart = isFiniteNumber(cue.startMs) ? Math.round(cue.startMs) : 0;
|
||||
const rawEnd = isFiniteNumber(cue.endMs) ? Math.round(cue.endMs) : rawStart + 1000;
|
||||
const startMs = Math.max(0, Math.min(rawStart, rawEnd));
|
||||
const endMs = Math.max(startMs + 1, rawEnd);
|
||||
const words: CaptionCueWord[] | undefined = Array.isArray(cue.words)
|
||||
? cue.words
|
||||
.filter(
|
||||
(word): word is CaptionCueWord => Boolean(word && typeof word.text === "string"),
|
||||
)
|
||||
.map((word) => {
|
||||
const rawWordStart = isFiniteNumber(word.startMs) ? Math.round(word.startMs) : startMs;
|
||||
const rawWordEnd = isFiniteNumber(word.endMs) ? Math.round(word.endMs) : rawWordStart + 1;
|
||||
const normalizedWordStart = clamp(rawWordStart, startMs, endMs - 1);
|
||||
const normalizedWordEnd = clamp(rawWordEnd, normalizedWordStart + 1, endMs);
|
||||
|
||||
return {
|
||||
text: word.text.trim(),
|
||||
startMs: normalizedWordStart,
|
||||
endMs: normalizedWordEnd,
|
||||
...(word.leadingSpace ? { leadingSpace: true } : {}),
|
||||
};
|
||||
})
|
||||
.filter((word) => word.text.length > 0)
|
||||
: undefined;
|
||||
|
||||
return {
|
||||
id: cue.id,
|
||||
startMs,
|
||||
endMs,
|
||||
text: typeof cue.text === "string" ? cue.text.trim() : "",
|
||||
...(words && words.length > 0 ? { words } : {}),
|
||||
};
|
||||
})
|
||||
.filter((cue) => cue.text.length > 0)
|
||||
: [];
|
||||
|
||||
const rawAutoCaptionSettings: Partial<AutoCaptionSettings> =
|
||||
editor.autoCaptionSettings && typeof editor.autoCaptionSettings === "object"
|
||||
? (editor.autoCaptionSettings as Partial<AutoCaptionSettings>)
|
||||
: {};
|
||||
const normalizedAutoCaptionSettings: AutoCaptionSettings = {
|
||||
enabled:
|
||||
typeof rawAutoCaptionSettings.enabled === "boolean"
|
||||
? rawAutoCaptionSettings.enabled
|
||||
: DEFAULT_AUTO_CAPTION_SETTINGS.enabled,
|
||||
language:
|
||||
typeof rawAutoCaptionSettings.language === "string" && rawAutoCaptionSettings.language.trim()
|
||||
? rawAutoCaptionSettings.language.trim()
|
||||
: DEFAULT_AUTO_CAPTION_SETTINGS.language,
|
||||
fontFamily:
|
||||
getDefaultCaptionFontFamily(),
|
||||
fontSize: isFiniteNumber(rawAutoCaptionSettings.fontSize)
|
||||
? clamp(rawAutoCaptionSettings.fontSize, 16, 72)
|
||||
: DEFAULT_AUTO_CAPTION_SETTINGS.fontSize,
|
||||
bottomOffset: isFiniteNumber(rawAutoCaptionSettings.bottomOffset)
|
||||
? clamp(rawAutoCaptionSettings.bottomOffset, 0, 30)
|
||||
: DEFAULT_AUTO_CAPTION_SETTINGS.bottomOffset,
|
||||
maxWidth: isFiniteNumber(rawAutoCaptionSettings.maxWidth)
|
||||
? clamp(rawAutoCaptionSettings.maxWidth, 40, 95)
|
||||
: DEFAULT_AUTO_CAPTION_SETTINGS.maxWidth,
|
||||
maxRows: isFiniteNumber(rawAutoCaptionSettings.maxRows)
|
||||
? clamp(Math.round(rawAutoCaptionSettings.maxRows), 1, 4)
|
||||
: DEFAULT_AUTO_CAPTION_SETTINGS.maxRows,
|
||||
animationStyle: normalizeAutoCaptionAnimation(
|
||||
rawAutoCaptionSettings.animationStyle,
|
||||
DEFAULT_AUTO_CAPTION_SETTINGS.animationStyle,
|
||||
),
|
||||
boxRadius: isFiniteNumber(rawAutoCaptionSettings.boxRadius)
|
||||
? clamp(rawAutoCaptionSettings.boxRadius, 0, 40)
|
||||
: DEFAULT_AUTO_CAPTION_SETTINGS.boxRadius,
|
||||
textColor:
|
||||
typeof rawAutoCaptionSettings.textColor === "string" && rawAutoCaptionSettings.textColor.trim()
|
||||
? rawAutoCaptionSettings.textColor
|
||||
: DEFAULT_AUTO_CAPTION_SETTINGS.textColor,
|
||||
inactiveTextColor:
|
||||
typeof rawAutoCaptionSettings.inactiveTextColor === "string" && rawAutoCaptionSettings.inactiveTextColor.trim()
|
||||
? rawAutoCaptionSettings.inactiveTextColor
|
||||
: DEFAULT_AUTO_CAPTION_SETTINGS.inactiveTextColor,
|
||||
backgroundOpacity: isFiniteNumber(rawAutoCaptionSettings.backgroundOpacity)
|
||||
? clamp(rawAutoCaptionSettings.backgroundOpacity, 0, 1)
|
||||
: DEFAULT_AUTO_CAPTION_SETTINGS.backgroundOpacity,
|
||||
};
|
||||
|
||||
const rawCropX = isFiniteNumber(editor.cropRegion?.x)
|
||||
? editor.cropRegion.x
|
||||
: DEFAULT_CROP_REGION.x;
|
||||
@@ -374,8 +528,26 @@ export function normalizeProjectEditor(editor: Partial<ProjectEditorState>): Pro
|
||||
backgroundBlur: normalizedBackgroundBlur,
|
||||
zoomMotionBlur: normalizedZoomMotionBlur,
|
||||
connectZooms: typeof editor.connectZooms === "boolean" ? editor.connectZooms : true,
|
||||
zoomInDurationMs: normalizedZoomInDurationMs,
|
||||
zoomInOverlapMs: normalizedZoomInOverlapMs,
|
||||
zoomOutDurationMs: normalizedZoomOutDurationMs,
|
||||
connectedZoomGapMs: normalizedConnectedZoomGapMs,
|
||||
connectedZoomDurationMs: normalizedConnectedZoomDurationMs,
|
||||
zoomInEasing: normalizeZoomTransitionEasing(editor.zoomInEasing, DEFAULT_ZOOM_IN_EASING),
|
||||
zoomOutEasing: normalizeZoomTransitionEasing(editor.zoomOutEasing, DEFAULT_ZOOM_OUT_EASING),
|
||||
connectedZoomEasing: normalizeZoomTransitionEasing(
|
||||
editor.connectedZoomEasing,
|
||||
DEFAULT_CONNECTED_ZOOM_EASING,
|
||||
),
|
||||
showCursor: typeof editor.showCursor === "boolean" ? editor.showCursor : true,
|
||||
loopCursor: typeof editor.loopCursor === "boolean" ? editor.loopCursor : false,
|
||||
cursorStyle:
|
||||
editor.cursorStyle === "dot" ||
|
||||
editor.cursorStyle === "figma" ||
|
||||
editor.cursorStyle === "mono" ||
|
||||
editor.cursorStyle === "tahoe"
|
||||
? editor.cursorStyle
|
||||
: DEFAULT_CURSOR_STYLE,
|
||||
cursorSize: isFiniteNumber(editor.cursorSize)
|
||||
? clamp(editor.cursorSize, 0.5, 10)
|
||||
: DEFAULT_CURSOR_SIZE,
|
||||
@@ -395,7 +567,7 @@ export function normalizeProjectEditor(editor: Partial<ProjectEditorState>): Pro
|
||||
? clamp((editor as Partial<ProjectEditorState>).cursorSway as number, 0, 2)
|
||||
: DEFAULT_CURSOR_SWAY,
|
||||
borderRadius: typeof editor.borderRadius === "number" ? editor.borderRadius : 12.5,
|
||||
padding: isFiniteNumber(editor.padding) ? clamp(editor.padding, 0, 100) : 50,
|
||||
padding: isFiniteNumber(editor.padding) ? clamp(editor.padding, 0, 100) : 20,
|
||||
cropRegion: {
|
||||
x: cropX,
|
||||
y: cropY,
|
||||
@@ -407,6 +579,8 @@ export function normalizeProjectEditor(editor: Partial<ProjectEditorState>): Pro
|
||||
speedRegions: normalizedSpeedRegions,
|
||||
annotationRegions: normalizedAnnotationRegions,
|
||||
audioRegions: normalizedAudioRegions,
|
||||
autoCaptions: normalizedAutoCaptions,
|
||||
autoCaptionSettings: normalizedAutoCaptionSettings,
|
||||
webcam: {
|
||||
enabled:
|
||||
typeof webcam.enabled === "boolean" ? webcam.enabled : DEFAULT_WEBCAM_OVERLAY.enabled,
|
||||
|
||||
Reference in New Issue
Block a user