mirror of
https://github.com/webadderallorg/Recordly.git
synced 2026-09-26 15:55:35 +00:00
fix: stabilize editor settings and extension UI
This commit is contained in:
Generated
+14
-5
@@ -9,6 +9,7 @@
|
||||
"version": "1.1.23",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@phosphor-icons/react": "^2.1.10",
|
||||
"capturekit": "^1.0.13",
|
||||
"electron-updater": "^6.8.3",
|
||||
"ffmpeg-static": "^5.3.0",
|
||||
@@ -2843,6 +2844,19 @@
|
||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@phosphor-icons/react": {
|
||||
"version": "2.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@phosphor-icons/react/-/react-2.1.10.tgz",
|
||||
"integrity": "sha512-vt8Tvq8GLjheAZZYa+YG/pW7HDbov8El/MANW8pOAz4eGxrwhnbfrQZq0Cp4q8zBEu8NIhHdnr+r8thnfRSNYA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">= 16.8",
|
||||
"react-dom": ">= 16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@pixi/color": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@pixi/color/-/color-7.4.3.tgz",
|
||||
@@ -9046,7 +9060,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/js-yaml": {
|
||||
@@ -9361,7 +9374,6 @@
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||
@@ -11227,7 +11239,6 @@
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
@@ -11240,7 +11251,6 @@
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
@@ -11948,7 +11958,6 @@
|
||||
"version": "0.23.2",
|
||||
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
|
||||
"integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"test:watch": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@phosphor-icons/react": "^2.1.10",
|
||||
"capturekit": "^1.0.13",
|
||||
"electron-updater": "^6.8.3",
|
||||
"ffmpeg-static": "^5.3.0",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,6 +40,7 @@ import type {
|
||||
CaptionCue,
|
||||
CropRegion,
|
||||
CursorStyle,
|
||||
EditorEffectSection,
|
||||
FigureData,
|
||||
PlaybackSpeed,
|
||||
WebcamOverlaySettings,
|
||||
@@ -111,19 +112,6 @@ const CAPTION_ANIMATION_OPTIONS: Array<{ value: AutoCaptionAnimation; label: str
|
||||
];
|
||||
|
||||
type BackgroundTab = "image" | "video" | "color" | "gradient";
|
||||
export type EditorEffectSection =
|
||||
| "scene"
|
||||
| "cursor"
|
||||
| "captions"
|
||||
| "webcam"
|
||||
| "settings"
|
||||
| "zoom"
|
||||
| "frame"
|
||||
| "crop"
|
||||
| "extensions"
|
||||
| "clip"
|
||||
| `ext:${string}`;
|
||||
|
||||
function isHexWallpaper(value: string): boolean {
|
||||
return /^#(?:[0-9a-f]{3}){1,2}$/i.test(value);
|
||||
}
|
||||
@@ -439,8 +427,6 @@ interface SettingsPanelProps {
|
||||
onSpeedDelete?: (id: string) => void;
|
||||
}
|
||||
|
||||
export default SettingsPanel;
|
||||
|
||||
const ZOOM_DEPTH_OPTIONS: Array<{ depth: ZoomDepth; label: string }> = [
|
||||
{ depth: 1, label: "1.25×" },
|
||||
{ depth: 2, label: "1.5×" },
|
||||
@@ -2291,13 +2277,13 @@ export function SettingsPanel({
|
||||
<p className="mt-1.5 text-[10px] text-slate-500">
|
||||
{selectedZoomMode === "manual"
|
||||
? tSettings(
|
||||
"zoom.modeManualDescription",
|
||||
"Set a fixed focus point for this zoom",
|
||||
)
|
||||
"zoom.modeManualDescription",
|
||||
"Set a fixed focus point for this zoom",
|
||||
)
|
||||
: tSettings(
|
||||
"zoom.modeAutoDescription",
|
||||
"Camera follows cursor automatically",
|
||||
)}
|
||||
"zoom.modeAutoDescription",
|
||||
"Camera follows cursor automatically",
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid grid-cols-6 gap-1.5">
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import {
|
||||
ArrowClockwise as Redo2,
|
||||
ArrowCounterClockwise as Undo2,
|
||||
Check,
|
||||
CaretDown as ChevronDown,
|
||||
CaretUp as ChevronUp,
|
||||
Check,
|
||||
ClosedCaptioning,
|
||||
Crop,
|
||||
Cursor,
|
||||
DownloadSimple as Download,
|
||||
FloppyDisk as Save,
|
||||
FolderOpen,
|
||||
Gear,
|
||||
MagicWand as WandSparkles,
|
||||
MagnifyingGlassPlus as ZoomIn,
|
||||
Pause,
|
||||
Camera as PhCameraRegular,
|
||||
Play,
|
||||
Plus,
|
||||
Camera as PhCameraRegular,
|
||||
PuzzlePiece,
|
||||
ArrowClockwise as Redo2,
|
||||
FloppyDisk as Save,
|
||||
Scissors,
|
||||
SkipBack,
|
||||
SkipForward,
|
||||
SpeakerHigh as Volume2,
|
||||
SpeakerLow as Volume1,
|
||||
SpeakerX as VolumeX,
|
||||
Sparkle,
|
||||
ArrowCounterClockwise as Undo2,
|
||||
UserCircle as User,
|
||||
SpeakerLow as Volume1,
|
||||
SpeakerHigh as Volume2,
|
||||
SpeakerX as VolumeX,
|
||||
MagicWand as WandSparkles,
|
||||
X,
|
||||
MagnifyingGlassPlus as ZoomIn,
|
||||
} from "@phosphor-icons/react";
|
||||
import type { Span } from "dnd-timeline";
|
||||
import { motion } from "motion/react";
|
||||
@@ -113,7 +113,7 @@ import {
|
||||
toFileUrl,
|
||||
validateProjectData,
|
||||
} from "./projectPersistence";
|
||||
import { type EditorEffectSection, SettingsPanel } from "./SettingsPanel";
|
||||
import { SettingsPanel } from "./SettingsPanel";
|
||||
import {
|
||||
APP_HEADER_ICON_BUTTON_CLASS,
|
||||
DiscordLinkButton,
|
||||
@@ -153,6 +153,7 @@ import {
|
||||
DEFAULT_ZOOM_IN_OVERLAP_MS,
|
||||
DEFAULT_ZOOM_OUT_DURATION_MS,
|
||||
DEFAULT_ZOOM_OUT_EASING,
|
||||
type EditorEffectSection,
|
||||
type FigureData,
|
||||
getClipSourceEndMs,
|
||||
type PlaybackSpeed,
|
||||
@@ -676,7 +677,7 @@ export default function VideoEditor() {
|
||||
window.clearTimeout(pendingFreshRecordingAutoSuggestTimeoutRef.current);
|
||||
pendingFreshRecordingAutoSuggestTimeoutRef.current = null;
|
||||
}
|
||||
}, [videoPath]);
|
||||
}, []);
|
||||
|
||||
// Auto-activate builtin extensions at editor startup (idempotent)
|
||||
useEffect(() => {
|
||||
@@ -783,7 +784,27 @@ export default function VideoEditor() {
|
||||
annotationRegions,
|
||||
autoCaptions,
|
||||
autoCaptionSettings,
|
||||
speedRegions: effectiveSpeedRegions,
|
||||
speedRegions: (() => {
|
||||
const clipDerived: SpeedRegion[] = clipRegions
|
||||
.filter((clip) => clip.speed !== 1)
|
||||
.map((clip) => ({
|
||||
id: `clip-speed-${clip.id}`,
|
||||
startMs: clip.startMs,
|
||||
endMs: getClipSourceEndMs(clip),
|
||||
speed: clip.speed as SpeedRegion["speed"],
|
||||
}));
|
||||
if (clipDerived.length === 0) return speedRegions;
|
||||
const result = [...speedRegions];
|
||||
for (const cs of clipDerived) {
|
||||
const overlaps = speedRegions.some(
|
||||
(sr) => sr.endMs > cs.startMs && sr.startMs < cs.endMs,
|
||||
);
|
||||
if (!overlaps) {
|
||||
result.push(cs);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
})(),
|
||||
previewWidth,
|
||||
previewHeight,
|
||||
cursorTelemetry,
|
||||
@@ -870,11 +891,11 @@ export default function VideoEditor() {
|
||||
cursorStyle,
|
||||
cursorSway,
|
||||
cursorTelemetry,
|
||||
clipRegions,
|
||||
padding,
|
||||
shadowIntensity,
|
||||
showCursor,
|
||||
speedRegions,
|
||||
clipRegions,
|
||||
wallpaper,
|
||||
webcam,
|
||||
zoomInDurationMs,
|
||||
@@ -884,6 +905,7 @@ export default function VideoEditor() {
|
||||
zoomOutDurationMs,
|
||||
zoomOutEasing,
|
||||
zoomRegions,
|
||||
zoomClassicMode,
|
||||
]);
|
||||
|
||||
const markExportAsSaving = useCallback(() => {
|
||||
@@ -1271,7 +1293,6 @@ export default function VideoEditor() {
|
||||
shadowIntensity,
|
||||
backgroundBlur,
|
||||
zoomMotionBlur,
|
||||
autoApplyFreshRecordingAutoZooms,
|
||||
connectZooms,
|
||||
zoomInDurationMs,
|
||||
zoomInOverlapMs,
|
||||
@@ -1313,6 +1334,7 @@ export default function VideoEditor() {
|
||||
gifFrameRate,
|
||||
gifLoop,
|
||||
gifSizePreset,
|
||||
frame,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -1729,8 +1751,9 @@ export default function VideoEditor() {
|
||||
setVideoPath(sourceVideoUrl);
|
||||
setCurrentProjectPath(null);
|
||||
setLastSavedSnapshot(null);
|
||||
pendingFreshRecordingAutoZoomPathRef.current =
|
||||
autoApplyFreshRecordingAutoZooms ? sourceVideoUrl : null;
|
||||
pendingFreshRecordingAutoZoomPathRef.current = autoApplyFreshRecordingAutoZooms
|
||||
? sourceVideoUrl
|
||||
: null;
|
||||
setWebcam((prev) => ({
|
||||
...prev,
|
||||
enabled: Boolean(sessionResult.session?.webcamPath),
|
||||
@@ -1747,8 +1770,9 @@ export default function VideoEditor() {
|
||||
setVideoPath(sourceVideoUrl);
|
||||
setCurrentProjectPath(null);
|
||||
setLastSavedSnapshot(null);
|
||||
pendingFreshRecordingAutoZoomPathRef.current =
|
||||
autoApplyFreshRecordingAutoZooms ? sourceVideoUrl : null;
|
||||
pendingFreshRecordingAutoZoomPathRef.current = autoApplyFreshRecordingAutoZooms
|
||||
? sourceVideoUrl
|
||||
: null;
|
||||
setWebcam((prev) => ({
|
||||
...prev,
|
||||
enabled: false,
|
||||
@@ -1767,8 +1791,13 @@ export default function VideoEditor() {
|
||||
loadInitialData();
|
||||
}, [
|
||||
applyLoadedProject,
|
||||
autoApplyFreshRecordingAutoZooms,
|
||||
initialEditorPreferences,
|
||||
smokeExportConfig.enabled,
|
||||
smokeExportConfig.inputPath,
|
||||
smokeExportConfig.webcamInputPath,
|
||||
smokeExportConfig.webcamShadow,
|
||||
smokeExportConfig.webcamSize,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -1839,8 +1868,6 @@ export default function VideoEditor() {
|
||||
cursorStyle,
|
||||
cursorSize,
|
||||
cursorSmoothing,
|
||||
zoomSmoothness,
|
||||
zoomClassicMode,
|
||||
cursorMotionBlur,
|
||||
cursorClickBounce,
|
||||
cursorClickBounceDuration,
|
||||
@@ -2362,7 +2389,6 @@ export default function VideoEditor() {
|
||||
speed: clip.speed as SpeedRegion["speed"],
|
||||
}));
|
||||
if (clipDerived.length === 0) return speedRegions;
|
||||
// Timeline speed regions take precedence; only fill in clip speed where no overlap exists
|
||||
const result = [...speedRegions];
|
||||
for (const cs of clipDerived) {
|
||||
const overlaps = speedRegions.some(
|
||||
@@ -3852,8 +3878,6 @@ export default function VideoEditor() {
|
||||
videoPath,
|
||||
wallpaper,
|
||||
trimRegions,
|
||||
speedRegions,
|
||||
clipRegions,
|
||||
shadowIntensity,
|
||||
backgroundBlur,
|
||||
zoomMotionBlur,
|
||||
@@ -3894,14 +3918,21 @@ export default function VideoEditor() {
|
||||
effectiveZoomRegions,
|
||||
ensureSupportedMp4SourceDimensions,
|
||||
markExportAsSaving,
|
||||
mp4FrameRate,
|
||||
remountPreview,
|
||||
showExportSuccessToast,
|
||||
smokeExportConfig.backendPreference,
|
||||
smokeExportConfig.enabled,
|
||||
smokeExportConfig.useNativeExport,
|
||||
smokeExportConfig.maxDecodeQueue,
|
||||
smokeExportConfig.maxEncodeQueue,
|
||||
smokeExportConfig.maxPendingFrames,
|
||||
smokeExportConfig.outputPath,
|
||||
smokeExportConfig.pipelineModel,
|
||||
smokeExportConfig.shadowIntensity,
|
||||
effectiveSpeedRegions,
|
||||
frame,
|
||||
smokeExportConfig.encodingMode,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -4593,9 +4624,7 @@ export default function VideoEditor() {
|
||||
whileHover={{ opacity: 1 }}
|
||||
initial={{ opacity: 0.55 }}
|
||||
>
|
||||
<motion.span
|
||||
className="absolute inset-0 rounded-lg bg-white/[0.04] opacity-0 transition group-hover:opacity-100"
|
||||
/>
|
||||
<motion.span className="absolute inset-0 rounded-lg bg-white/[0.04] opacity-0 transition group-hover:opacity-100" />
|
||||
<User className="relative z-10 h-[22px] w-[22px]" />
|
||||
</motion.button>
|
||||
</div>
|
||||
@@ -4921,7 +4950,9 @@ export default function VideoEditor() {
|
||||
className="h-7 gap-1 rounded-full border border-white/[0.08] bg-white/[0.04] px-2.5 text-[11px] text-white/65 shadow-[inset_0_1px_0_rgba(255,255,255,0.06)] transition-all hover:bg-white/[0.08] hover:text-white"
|
||||
>
|
||||
<Plus className="w-3.5 h-3.5" />
|
||||
<span className="font-medium">{t("editor.toolbar.addLayer")}</span>
|
||||
<span className="font-medium">
|
||||
{t("editor.toolbar.addLayer")}
|
||||
</span>
|
||||
<ChevronDown className="w-3 h-3" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export { default as VideoEditor } from './VideoEditor';
|
||||
export { default as VideoPlayback } from './VideoPlayback';
|
||||
export { default as PlaybackControls } from './PlaybackControls';
|
||||
export { default as TimelineEditor } from './timeline/TimelineEditor';
|
||||
export { default as SettingsPanel } from './SettingsPanel';
|
||||
|
||||
export { default as PlaybackControls } from "./PlaybackControls";
|
||||
export { SettingsPanel } from "./SettingsPanel";
|
||||
export { default as TimelineEditor } from "./timeline/TimelineEditor";
|
||||
export { default as VideoEditor } from "./VideoEditor";
|
||||
export { default as VideoPlayback } from "./VideoPlayback";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,7 +20,13 @@ export interface CursorTelemetryPoint {
|
||||
timeMs: number;
|
||||
cx: number;
|
||||
cy: number;
|
||||
interactionType?: "move" | "click" | "double-click" | "right-click" | "middle-click" | "mouseup";
|
||||
interactionType?:
|
||||
| "move"
|
||||
| "click"
|
||||
| "double-click"
|
||||
| "right-click"
|
||||
| "middle-click"
|
||||
| "mouseup";
|
||||
cursorType?:
|
||||
| "arrow"
|
||||
| "text"
|
||||
@@ -43,14 +49,22 @@ export interface CursorVisualSettings {
|
||||
style: CursorStyle;
|
||||
}
|
||||
|
||||
export type CursorStyle =
|
||||
| "tahoe"
|
||||
| "dot"
|
||||
| "figma"
|
||||
| "mono"
|
||||
| (string & {}); // extension-contributed cursor styles
|
||||
export type CursorStyle = "tahoe" | "dot" | "figma" | "mono" | (string & {}); // extension-contributed cursor styles
|
||||
export const DEFAULT_CURSOR_STYLE: CursorStyle = "tahoe";
|
||||
|
||||
export type EditorEffectSection =
|
||||
| "scene"
|
||||
| "cursor"
|
||||
| "captions"
|
||||
| "webcam"
|
||||
| "settings"
|
||||
| "zoom"
|
||||
| "frame"
|
||||
| "crop"
|
||||
| "extensions"
|
||||
| "clip"
|
||||
| `ext:${string}`;
|
||||
|
||||
export type ZoomTransitionEasing = "recordly" | "glide" | "smooth" | "snappy" | "linear";
|
||||
|
||||
export type WebcamCorner = "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
||||
@@ -183,7 +197,6 @@ export const BLUR_ANNOTATION_STRENGTH = 20;
|
||||
export const BASE_PREVIEW_WIDTH = 1920;
|
||||
export const BASE_PREVIEW_HEIGHT = 1080;
|
||||
|
||||
|
||||
export type ArrowDirection =
|
||||
| "up"
|
||||
| "down"
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"noMicrophonesFound": "마이크를 찾을 수 없습니다",
|
||||
"webcam": "웹캠",
|
||||
"turnOffWebcam": "웹캠 끄기",
|
||||
"hideFloatingWebcamPreview": "플로팅 미리보기 숨기기",
|
||||
"showFloatingWebcamPreview": "플로팅 미리보기 표시",
|
||||
"selectWebcamToEnable": "사용할 웹캠을 선택하세요",
|
||||
"noWebcamsFound": "웹캠을 찾을 수 없습니다",
|
||||
"recordingsFolder": "녹화 폴더",
|
||||
|
||||
@@ -159,6 +159,7 @@
|
||||
"high": "높음",
|
||||
"original": "원본"
|
||||
},
|
||||
"fpsTitle": "FPS",
|
||||
"loop": "반복",
|
||||
"outputDimensions": "출력: {{dimensions}}px",
|
||||
"sizePresetOriginalShort": "원본",
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"noMicrophonesFound": "Geen microfoons gevonden",
|
||||
"webcam": "Webcam",
|
||||
"turnOffWebcam": "Webcam uitschakelen",
|
||||
"hideFloatingWebcamPreview": "Zwevende voorbeeldweergave verbergen",
|
||||
"showFloatingWebcamPreview": "Zwevende voorbeeldweergave tonen",
|
||||
"selectWebcamToEnable": "Selecteer een webcam om in te schakelen",
|
||||
"noWebcamsFound": "Geen webcams gevonden",
|
||||
"recordingsFolder": "Opnamepad",
|
||||
|
||||
Reference in New Issue
Block a user