mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-27 00:05:39 +00:00
fix: address PR feedback regressions
This commit is contained in:
@@ -1448,9 +1448,9 @@ export function SettingsPanel({
|
||||
|
||||
const resetZoomSection = () => {
|
||||
onZoomTemporalMotionBlurChange?.(initialEditorPreferences.zoomTemporalMotionBlur);
|
||||
onZoomMotionBlurSampleCountChange?.(initialEditorPreferences.zoomMotionBlurSampleCount);
|
||||
onZoomMotionBlurSampleCountChange?.(TEMPORAL_MOTION_BLUR_DEFAULT_SAMPLE_COUNT);
|
||||
onZoomMotionBlurShutterFractionChange?.(
|
||||
initialEditorPreferences.zoomMotionBlurShutterFraction,
|
||||
TEMPORAL_MOTION_BLUR_DEFAULT_SHUTTER_FRACTION,
|
||||
);
|
||||
onZoomInDurationMsChange?.(initialEditorPreferences.zoomInDurationMs);
|
||||
onZoomOutDurationMsChange?.(initialEditorPreferences.zoomOutDurationMs);
|
||||
@@ -1481,7 +1481,6 @@ export function SettingsPanel({
|
||||
MOTION_PRESET_ORDER.find((presetId) => {
|
||||
const preset = CURSOR_MOTION_PRESETS[presetId];
|
||||
return (
|
||||
preset.zoomSmoothness === DEFAULT_ZOOM_SMOOTHNESS &&
|
||||
preset.zoomInDurationMs === zoomInDurationMs &&
|
||||
preset.zoomOutDurationMs === zoomOutDurationMs &&
|
||||
preset.cursorSize === cursorSize &&
|
||||
|
||||
@@ -77,6 +77,7 @@ import {
|
||||
getAspectRatioLabel,
|
||||
getAspectRatioValue,
|
||||
} from "@/utils/aspectRatioUtils";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ExtensionIcon } from "./ExtensionIcon";
|
||||
|
||||
const PhCursorFill = (props: { className?: string; weight?: "fill" | "regular" }) => (
|
||||
@@ -103,7 +104,10 @@ import { resolveAutoCaptionSourcePath } from "./autoCaptionSource";
|
||||
import { CropControl } from "./CropControl";
|
||||
import { ExportSettingsMenu } from "./ExportSettingsMenu";
|
||||
import ExtensionManager from "./ExtensionManager";
|
||||
import { loadEditorPreferences, saveEditorPreferences } from "./editorPreferences";
|
||||
import {
|
||||
loadEditorPreferences,
|
||||
saveEditorPreferences,
|
||||
} from "./editorPreferences";
|
||||
import ProjectBrowserDialog, { type ProjectLibraryEntry } from "./ProjectBrowserDialog";
|
||||
import {
|
||||
createProjectData,
|
||||
@@ -622,6 +626,18 @@ export default function VideoEditor() {
|
||||
const [cursorSmoothing, setCursorSmoothing] = useState(
|
||||
initialEditorPreferences.cursorSmoothing,
|
||||
);
|
||||
const [cursorSpringStiffnessMultiplier, setCursorSpringStiffnessMultiplier] = useState(
|
||||
initialEditorPreferences.cursorSpringStiffnessMultiplier,
|
||||
);
|
||||
const [cursorSpringDampingMultiplier, setCursorSpringDampingMultiplier] = useState(
|
||||
initialEditorPreferences.cursorSpringDampingMultiplier,
|
||||
);
|
||||
const [cursorSpringMassMultiplier, setCursorSpringMassMultiplier] = useState(
|
||||
initialEditorPreferences.cursorSpringMassMultiplier,
|
||||
);
|
||||
const [sessionShowCursorOverride, setSessionShowCursorOverride] = useState<boolean | null>(
|
||||
null,
|
||||
);
|
||||
const [zoomSmoothness, setZoomSmoothness] = useState(0.5);
|
||||
const [zoomClassicMode, setZoomClassicMode] = useState(false);
|
||||
const [cursorMotionBlur, setCursorMotionBlur] = useState(
|
||||
@@ -685,6 +701,7 @@ export default function VideoEditor() {
|
||||
const [sourceAudioFallbackPaths, setSourceAudioFallbackPaths] = useState<string[]>([]);
|
||||
const [sourceAudioFallbackStartDelayMsByPath, setSourceAudioFallbackStartDelayMsByPath] =
|
||||
useState<Record<string, number>>({});
|
||||
const effectiveShowCursor = sessionShowCursorOverride ?? showCursor;
|
||||
const [aspectRatio, setAspectRatio] = useState<AspectRatio>(
|
||||
initialEditorPreferences.aspectRatio,
|
||||
);
|
||||
@@ -917,10 +934,13 @@ export default function VideoEditor() {
|
||||
previewWidth,
|
||||
previewHeight,
|
||||
cursorTelemetry,
|
||||
showCursor,
|
||||
effectiveShowCursor,
|
||||
cursorStyle,
|
||||
cursorSize,
|
||||
cursorSmoothing,
|
||||
cursorSpringStiffnessMultiplier,
|
||||
cursorSpringDampingMultiplier,
|
||||
cursorSpringMassMultiplier,
|
||||
zoomSmoothness,
|
||||
zoomClassicMode,
|
||||
cursorMotionBlur,
|
||||
@@ -996,6 +1016,9 @@ export default function VideoEditor() {
|
||||
cursorMotionBlur,
|
||||
cursorSize,
|
||||
cursorSmoothing,
|
||||
cursorSpringDampingMultiplier,
|
||||
cursorSpringMassMultiplier,
|
||||
cursorSpringStiffnessMultiplier,
|
||||
zoomSmoothness,
|
||||
cursorStyle,
|
||||
cursorSway,
|
||||
@@ -1004,7 +1027,7 @@ export default function VideoEditor() {
|
||||
padding,
|
||||
resolvedWebcamVideoUrl,
|
||||
shadowIntensity,
|
||||
showCursor,
|
||||
effectiveShowCursor,
|
||||
speedRegions,
|
||||
wallpaper,
|
||||
webcam,
|
||||
@@ -1032,6 +1055,11 @@ export default function VideoEditor() {
|
||||
}));
|
||||
}, []);
|
||||
|
||||
const handleShowCursorChange = useCallback((nextShowCursor: boolean) => {
|
||||
setSessionShowCursorOverride(null);
|
||||
setShowCursor(nextShowCursor);
|
||||
}, []);
|
||||
|
||||
const remountPreview = useCallback(() => {
|
||||
setIsPreviewReady(false);
|
||||
setPreviewVersion((version) => version + 1);
|
||||
@@ -1323,6 +1351,9 @@ export default function VideoEditor() {
|
||||
cursorStyle: CursorStyle;
|
||||
cursorSize: number;
|
||||
cursorSmoothing: number;
|
||||
cursorSpringStiffnessMultiplier: number;
|
||||
cursorSpringDampingMultiplier: number;
|
||||
cursorSpringMassMultiplier: number;
|
||||
zoomSmoothness: number;
|
||||
zoomClassicMode: boolean;
|
||||
cursorMotionBlur: number;
|
||||
@@ -1471,6 +1502,9 @@ export default function VideoEditor() {
|
||||
cursorStyle,
|
||||
cursorSize,
|
||||
cursorSmoothing,
|
||||
cursorSpringStiffnessMultiplier,
|
||||
cursorSpringDampingMultiplier,
|
||||
cursorSpringMassMultiplier,
|
||||
zoomSmoothness,
|
||||
zoomClassicMode,
|
||||
cursorMotionBlur,
|
||||
@@ -1521,6 +1555,9 @@ export default function VideoEditor() {
|
||||
cursorStyle,
|
||||
cursorSize,
|
||||
cursorSmoothing,
|
||||
cursorSpringStiffnessMultiplier,
|
||||
cursorSpringDampingMultiplier,
|
||||
cursorSpringMassMultiplier,
|
||||
zoomSmoothness,
|
||||
zoomClassicMode,
|
||||
cursorMotionBlur,
|
||||
@@ -1694,11 +1731,19 @@ export default function VideoEditor() {
|
||||
setZoomInEasing(normalizedEditor.zoomInEasing);
|
||||
setZoomOutEasing(normalizedEditor.zoomOutEasing);
|
||||
setConnectedZoomEasing(normalizedEditor.connectedZoomEasing);
|
||||
setSessionShowCursorOverride(null);
|
||||
setShowCursor(normalizedEditor.showCursor);
|
||||
setLoopCursor(normalizedEditor.loopCursor);
|
||||
setCursorStyle(normalizedEditor.cursorStyle);
|
||||
setCursorSize(normalizedEditor.cursorSize);
|
||||
setCursorSmoothing(normalizedEditor.cursorSmoothing);
|
||||
setCursorSpringStiffnessMultiplier(
|
||||
normalizedEditor.cursorSpringStiffnessMultiplier,
|
||||
);
|
||||
setCursorSpringDampingMultiplier(
|
||||
normalizedEditor.cursorSpringDampingMultiplier,
|
||||
);
|
||||
setCursorSpringMassMultiplier(normalizedEditor.cursorSpringMassMultiplier);
|
||||
setZoomSmoothness(normalizedEditor.zoomSmoothness);
|
||||
setZoomClassicMode(normalizedEditor.zoomClassicMode);
|
||||
setCursorMotionBlur(normalizedEditor.cursorMotionBlur);
|
||||
@@ -2005,9 +2050,9 @@ export default function VideoEditor() {
|
||||
pendingFreshRecordingAutoZoomPathRef.current = autoApplyFreshRecordingAutoZooms
|
||||
? sourceVideoUrl
|
||||
: null;
|
||||
if (sessionResult.session.hideOverlayCursorByDefault) {
|
||||
setShowCursor(false);
|
||||
}
|
||||
setSessionShowCursorOverride(
|
||||
sessionResult.session.hideOverlayCursorByDefault ? false : null,
|
||||
);
|
||||
setWebcam((prev) => ({
|
||||
...prev,
|
||||
enabled: Boolean(sessionResult.session?.webcamPath),
|
||||
@@ -2027,6 +2072,7 @@ export default function VideoEditor() {
|
||||
setCurrentProjectPath(null);
|
||||
setLastSavedSnapshot(null);
|
||||
pendingFreshRecordingAutoZoomPathRef.current = null;
|
||||
setSessionShowCursorOverride(null);
|
||||
setWebcam((prev) => ({
|
||||
...prev,
|
||||
enabled: false,
|
||||
@@ -2097,6 +2143,9 @@ export default function VideoEditor() {
|
||||
cursorStyle,
|
||||
cursorSize,
|
||||
cursorSmoothing,
|
||||
cursorSpringStiffnessMultiplier,
|
||||
cursorSpringDampingMultiplier,
|
||||
cursorSpringMassMultiplier,
|
||||
cursorMotionBlur,
|
||||
cursorClickBounce,
|
||||
cursorClickBounceDuration,
|
||||
@@ -2138,6 +2187,9 @@ export default function VideoEditor() {
|
||||
cursorStyle,
|
||||
cursorSize,
|
||||
cursorSmoothing,
|
||||
cursorSpringStiffnessMultiplier,
|
||||
cursorSpringDampingMultiplier,
|
||||
cursorSpringMassMultiplier,
|
||||
cursorMotionBlur,
|
||||
cursorClickBounce,
|
||||
cursorClickBounceDuration,
|
||||
@@ -3975,10 +4027,13 @@ export default function VideoEditor() {
|
||||
autoCaptionSettings,
|
||||
zoomRegions: effectiveZoomRegions,
|
||||
cursorTelemetry: effectiveCursorTelemetry,
|
||||
showCursor,
|
||||
effectiveShowCursor,
|
||||
cursorStyle,
|
||||
cursorSize,
|
||||
cursorSmoothing,
|
||||
cursorSpringStiffnessMultiplier,
|
||||
cursorSpringDampingMultiplier,
|
||||
cursorSpringMassMultiplier,
|
||||
zoomSmoothness,
|
||||
zoomClassicMode,
|
||||
cursorMotionBlur,
|
||||
@@ -4147,10 +4202,13 @@ export default function VideoEditor() {
|
||||
autoCaptionSettings,
|
||||
zoomRegions: effectiveZoomRegions,
|
||||
cursorTelemetry: effectiveCursorTelemetry,
|
||||
showCursor,
|
||||
effectiveShowCursor,
|
||||
cursorStyle,
|
||||
cursorSize,
|
||||
cursorSmoothing,
|
||||
cursorSpringStiffnessMultiplier,
|
||||
cursorSpringDampingMultiplier,
|
||||
cursorSpringMassMultiplier,
|
||||
zoomSmoothness,
|
||||
zoomClassicMode,
|
||||
cursorMotionBlur,
|
||||
@@ -4383,11 +4441,14 @@ export default function VideoEditor() {
|
||||
zoomInEasing,
|
||||
zoomOutEasing,
|
||||
connectedZoomEasing,
|
||||
showCursor,
|
||||
effectiveShowCursor,
|
||||
cursorStyle,
|
||||
effectiveCursorTelemetry,
|
||||
cursorSize,
|
||||
cursorSmoothing,
|
||||
cursorSpringStiffnessMultiplier,
|
||||
cursorSpringDampingMultiplier,
|
||||
cursorSpringMassMultiplier,
|
||||
zoomSmoothness,
|
||||
zoomClassicMode,
|
||||
cursorMotionBlur,
|
||||
@@ -5255,8 +5316,8 @@ export default function VideoEditor() {
|
||||
onZoomOutEasingChange={setZoomOutEasing}
|
||||
connectedZoomEasing={connectedZoomEasing}
|
||||
onConnectedZoomEasingChange={setConnectedZoomEasing}
|
||||
showCursor={showCursor}
|
||||
onShowCursorChange={setShowCursor}
|
||||
showCursor={effectiveShowCursor}
|
||||
onShowCursorChange={handleShowCursorChange}
|
||||
loopCursor={loopCursor}
|
||||
onLoopCursorChange={setLoopCursor}
|
||||
cursorStyle={cursorStyle}
|
||||
@@ -5265,6 +5326,16 @@ export default function VideoEditor() {
|
||||
onCursorSizeChange={setCursorSize}
|
||||
cursorSmoothing={cursorSmoothing}
|
||||
onCursorSmoothingChange={setCursorSmoothing}
|
||||
cursorSpringStiffnessMultiplier={cursorSpringStiffnessMultiplier}
|
||||
onCursorSpringStiffnessMultiplierChange={
|
||||
setCursorSpringStiffnessMultiplier
|
||||
}
|
||||
cursorSpringDampingMultiplier={cursorSpringDampingMultiplier}
|
||||
onCursorSpringDampingMultiplierChange={
|
||||
setCursorSpringDampingMultiplier
|
||||
}
|
||||
cursorSpringMassMultiplier={cursorSpringMassMultiplier}
|
||||
onCursorSpringMassMultiplierChange={setCursorSpringMassMultiplier}
|
||||
zoomClassicMode={zoomClassicMode}
|
||||
onZoomClassicModeChange={setZoomClassicMode}
|
||||
cursorMotionBlur={cursorMotionBlur}
|
||||
@@ -5455,10 +5526,17 @@ export default function VideoEditor() {
|
||||
}
|
||||
onAnnotationSizeChange={handleAnnotationSizeChange}
|
||||
cursorTelemetry={effectiveCursorTelemetry}
|
||||
showCursor={showCursor}
|
||||
showCursor={effectiveShowCursor}
|
||||
cursorStyle={cursorStyle}
|
||||
cursorSize={cursorSize}
|
||||
cursorSmoothing={cursorSmoothing}
|
||||
cursorSpringStiffnessMultiplier={
|
||||
cursorSpringStiffnessMultiplier
|
||||
}
|
||||
cursorSpringDampingMultiplier={
|
||||
cursorSpringDampingMultiplier
|
||||
}
|
||||
cursorSpringMassMultiplier={cursorSpringMassMultiplier}
|
||||
zoomSmoothness={zoomSmoothness}
|
||||
zoomClassicMode={zoomClassicMode}
|
||||
cursorMotionBlur={cursorMotionBlur}
|
||||
|
||||
@@ -1102,7 +1102,7 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
return;
|
||||
}
|
||||
|
||||
videoEffectsContainer.filters = null;
|
||||
videoEffectsContainer.filters = [zoomBlurFilter, motionBlurFilter];
|
||||
motionBlurFilter.velocity = { x: 0, y: 0 };
|
||||
motionBlurFilter.kernelSize = 5;
|
||||
motionBlurFilter.offset = 0;
|
||||
@@ -1443,6 +1443,10 @@ const VideoPlayback = forwardRef<VideoPlaybackRef, VideoPlaybackProps>(
|
||||
videoEffectsContainerRef.current = videoEffectsContainer;
|
||||
zoomBlurFilterRef.current = new ZoomBlurFilter({ strength: 0 });
|
||||
motionBlurFilterRef.current = new MotionBlurFilter([0, 0], 5, 0);
|
||||
videoEffectsContainer.filters = [
|
||||
zoomBlurFilterRef.current,
|
||||
motionBlurFilterRef.current,
|
||||
];
|
||||
cameraContainer.addChild(videoEffectsContainer);
|
||||
syncPreviewMotionBlurQuality();
|
||||
|
||||
|
||||
@@ -443,14 +443,16 @@ export function loadEditorPresets(): EditorPreset[] {
|
||||
}
|
||||
}
|
||||
|
||||
export function saveEditorPresets(presets: EditorPreset[]): void {
|
||||
export function saveEditorPresets(presets: EditorPreset[]): boolean {
|
||||
if (typeof globalThis.localStorage === "undefined") {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
globalThis.localStorage.setItem(EDITOR_PRESETS_STORAGE_KEY, JSON.stringify(presets));
|
||||
return true;
|
||||
} catch {
|
||||
// Ignore storage failures so editor controls still work.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1476,21 +1476,18 @@ const TimelineEditor = forwardRef<TimelineEditorHandle, TimelineEditorProps>(
|
||||
}
|
||||
|
||||
const startPos = Math.max(0, Math.min(startMs, totalMs));
|
||||
const activeClip = clipRegions.find(
|
||||
(clip) => startPos >= clip.startMs && startPos < clip.endMs,
|
||||
);
|
||||
const nextClip = clipRegions.find((clip) => clip.startMs > startPos);
|
||||
const activeClip =
|
||||
clipRegions.length === 0
|
||||
? { startMs: 0, endMs: totalMs }
|
||||
: clipRegions.find((clip) => startPos >= clip.startMs && startPos < clip.endMs);
|
||||
if (!activeClip) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const sorted = [...zoomRegions].sort((a, b) => a.startMs - b.startMs);
|
||||
const nextRegion = sorted.find((region) => region.startMs > startPos);
|
||||
const gapToNextClipEdge = clipRegions.length === 0
|
||||
? totalMs - startPos
|
||||
: activeClip
|
||||
? activeClip.endMs - startPos
|
||||
: nextClip
|
||||
? nextClip.startMs - startPos
|
||||
: 0;
|
||||
const gapToNextRegion = nextRegion ? nextRegion.startMs - startPos : totalMs - startPos;
|
||||
const gapToNextClipEdge = activeClip.endMs - startPos;
|
||||
const gapToNextRegion = nextRegion ? nextRegion.startMs - startPos : gapToNextClipEdge;
|
||||
const availableDuration = Math.min(gapToNextClipEdge, gapToNextRegion);
|
||||
|
||||
const isOverlapping = sorted.some(
|
||||
|
||||
@@ -72,9 +72,9 @@ describe("computeCursorFollowFocus", () => {
|
||||
{ snapToEdgesRatio: 0.25 },
|
||||
);
|
||||
|
||||
expect(firstShift.cx).toBeCloseTo(0.7, 6);
|
||||
expect(firstShift.cx).toBeCloseTo(0.575, 6);
|
||||
expect(firstShift.cy).toBeCloseTo(0.5, 6);
|
||||
expect(secondShift.cx).toBeCloseTo(0.7, 6);
|
||||
expect(secondShift.cx).toBeCloseTo(0.595, 6);
|
||||
expect(secondShift.cy).toBeCloseTo(0.5, 6);
|
||||
});
|
||||
|
||||
@@ -107,4 +107,45 @@ describe("computeCursorFollowFocus", () => {
|
||||
|
||||
expect(clampedFocus).toEqual({ cx: 0.75, cy: 0.75 });
|
||||
});
|
||||
|
||||
it("reinitializes focus when time goes backward during zoom-out", () => {
|
||||
const state = createCursorFollowCameraState();
|
||||
const cursorSamples = [
|
||||
{ timeMs: 0, cx: 0.5, cy: 0.5, interactionType: "move" as const },
|
||||
{ timeMs: 100, cx: 0.8, cy: 0.5, interactionType: "move" as const },
|
||||
];
|
||||
|
||||
computeCursorFollowFocus(
|
||||
state,
|
||||
cursorSamples,
|
||||
100,
|
||||
2,
|
||||
1,
|
||||
{ cx: 0.5, cy: 0.5 },
|
||||
{ snapToEdgesRatio: 0.25 },
|
||||
);
|
||||
|
||||
const frozenFocus = computeCursorFollowFocus(
|
||||
state,
|
||||
cursorSamples,
|
||||
140,
|
||||
2,
|
||||
0.8,
|
||||
{ cx: 0.5, cy: 0.5 },
|
||||
{ snapToEdgesRatio: 0.25 },
|
||||
);
|
||||
|
||||
const rewoundFocus = computeCursorFollowFocus(
|
||||
state,
|
||||
cursorSamples,
|
||||
0,
|
||||
2,
|
||||
0.8,
|
||||
{ cx: 0.5, cy: 0.5 },
|
||||
{ snapToEdgesRatio: 0.25 },
|
||||
);
|
||||
|
||||
expect(frozenFocus.cx).toBeCloseTo(0.675, 6);
|
||||
expect(rewoundFocus).toEqual({ cx: 0.5, cy: 0.5 });
|
||||
});
|
||||
});
|
||||
@@ -100,15 +100,15 @@ function recenterFocusWhenCursorLeavesSafeZone(
|
||||
let nextFocusY = currentFocus.cy;
|
||||
|
||||
if (cursorFocus.cx < safeLeft) {
|
||||
nextFocusX = cursorFocus.cx;
|
||||
nextFocusX = cursorFocus.cx + halfSpan - safeZoneInset;
|
||||
} else if (cursorFocus.cx > safeRight) {
|
||||
nextFocusX = cursorFocus.cx;
|
||||
nextFocusX = cursorFocus.cx - halfSpan + safeZoneInset;
|
||||
}
|
||||
|
||||
if (cursorFocus.cy < safeTop) {
|
||||
nextFocusY = cursorFocus.cy;
|
||||
nextFocusY = cursorFocus.cy + halfSpan - safeZoneInset;
|
||||
} else if (cursorFocus.cy > safeBottom) {
|
||||
nextFocusY = cursorFocus.cy;
|
||||
nextFocusY = cursorFocus.cy - halfSpan + safeZoneInset;
|
||||
}
|
||||
|
||||
return clampFocusToScale(
|
||||
@@ -157,18 +157,21 @@ export function computeCursorFollowFocus(
|
||||
: clampedRegionFocus;
|
||||
}
|
||||
|
||||
const timeWentBackwards = state.initialized && timeMs + 0.5 < state.lastTimeMs;
|
||||
if (timeWentBackwards) {
|
||||
state.reachedFullZoom = false;
|
||||
}
|
||||
|
||||
// Track when zoom reaches full strength
|
||||
if (zoomStrength >= 0.99) {
|
||||
state.reachedFullZoom = true;
|
||||
}
|
||||
|
||||
// Zooming out: was fully zoomed but strength is now dropping — freeze camera
|
||||
if (state.reachedFullZoom && zoomStrength < 0.99) {
|
||||
if (!timeWentBackwards && state.reachedFullZoom && zoomStrength < 0.99) {
|
||||
return { cx: state.frozenFocusX, cy: state.frozenFocusY };
|
||||
}
|
||||
|
||||
const timeWentBackwards = state.initialized && timeMs + 0.5 < state.lastTimeMs;
|
||||
|
||||
if (!state.initialized || !state.wasZoomed || timeWentBackwards) {
|
||||
const initialFocus = recenterFocusWhenCursorLeavesSafeZone(
|
||||
clampedRegionFocus,
|
||||
|
||||
@@ -174,12 +174,13 @@ function computeZoomBlurGeometry({
|
||||
Math.hypot(focusStagePxX, stageSize.height - focusStagePxY),
|
||||
Math.hypot(stageSize.width - focusStagePxX, stageSize.height - focusStagePxY),
|
||||
);
|
||||
const radius = Math.max(outerRadius, 1);
|
||||
|
||||
return {
|
||||
centerX: focusStagePxX,
|
||||
centerY: focusStagePxY,
|
||||
innerRadius: Math.max(18, Math.min(outerRadius - 1, visibleHalfDiagonal)),
|
||||
radius: Math.max(outerRadius, 1),
|
||||
innerRadius: Math.max(0, Math.min(Math.max(18, Math.min(radius - 1, visibleHalfDiagonal)), radius - 1)),
|
||||
radius,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1103,6 +1103,8 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
|
||||
}
|
||||
}
|
||||
|
||||
hideEditorOverlayCursorByDefault.current = true;
|
||||
|
||||
const wantsAudioCapture = microphoneEnabled || systemAudioEnabled;
|
||||
const browserCaptureSource = await resolveBrowserCaptureSource(selectedSource);
|
||||
|
||||
@@ -1258,20 +1260,25 @@ export function useScreenRecorder(): UseScreenRecorderReturn {
|
||||
stream.current.addTrack(micAudioTrack);
|
||||
}
|
||||
} else {
|
||||
const mediaStream = await mediaDevices.getDisplayMedia({
|
||||
audio: false,
|
||||
video: {
|
||||
displaySurface: selectedSource.id?.startsWith("window:")
|
||||
? "window"
|
||||
: "monitor",
|
||||
width: { ideal: TARGET_WIDTH, max: TARGET_WIDTH },
|
||||
height: { ideal: TARGET_HEIGHT, max: TARGET_HEIGHT },
|
||||
frameRate: { ideal: TARGET_FRAME_RATE, max: TARGET_FRAME_RATE },
|
||||
cursor: "never",
|
||||
},
|
||||
selfBrowserSurface: "exclude",
|
||||
surfaceSwitching: "exclude",
|
||||
});
|
||||
const mediaStream = useLinuxPortal
|
||||
? await mediaDevices.getDisplayMedia({
|
||||
audio: false,
|
||||
video: {
|
||||
displaySurface: selectedSource.id?.startsWith("window:")
|
||||
? "window"
|
||||
: "monitor",
|
||||
width: { ideal: TARGET_WIDTH, max: TARGET_WIDTH },
|
||||
height: { ideal: TARGET_HEIGHT, max: TARGET_HEIGHT },
|
||||
frameRate: { ideal: TARGET_FRAME_RATE, max: TARGET_FRAME_RATE },
|
||||
cursor: "never",
|
||||
},
|
||||
selfBrowserSurface: "exclude",
|
||||
surfaceSwitching: "exclude",
|
||||
})
|
||||
: await mediaDevices.getUserMedia({
|
||||
audio: false,
|
||||
video: browserScreenVideoConstraints,
|
||||
});
|
||||
|
||||
stream.current = mediaStream;
|
||||
videoTrack = mediaStream.getVideoTracks()[0];
|
||||
|
||||
@@ -234,6 +234,7 @@ interface CaptionRenderState {
|
||||
interface RenderSnapshot {
|
||||
timeMs: number;
|
||||
cursorTimeMs: number;
|
||||
backgroundTimelineTimeMs: number;
|
||||
sceneTransform: { scale: number; x: number; y: number };
|
||||
zoom: { scale: number; focusX: number; focusY: number; progress: number };
|
||||
}
|
||||
@@ -2550,6 +2551,7 @@ export class FrameRenderer {
|
||||
return {
|
||||
timeMs,
|
||||
cursorTimeMs,
|
||||
backgroundTimelineTimeMs: backgroundTimelineTimestamp / 1000,
|
||||
sceneTransform: {
|
||||
scale: this.animationState.appliedScale,
|
||||
x: this.animationState.x,
|
||||
@@ -2635,6 +2637,17 @@ export class FrameRenderer {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (resolvedSnapshot !== lastSnapshot) {
|
||||
await this.renderSceneSample(
|
||||
Math.round(resolvedSnapshot.timeMs * 1000),
|
||||
Math.round(resolvedSnapshot.cursorTimeMs * 1000),
|
||||
Math.round(resolvedSnapshot.backgroundTimelineTimeMs * 1000),
|
||||
layoutCache,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
const hasOverlayCanvasWork =
|
||||
(this.config.annotationRegions?.length ?? 0) > 0 ||
|
||||
Boolean(this.captionCanvas && this.captionSprite?.visible);
|
||||
|
||||
Reference in New Issue
Block a user