From 2993a57853dbcbe00063a4090471be35b600f379 Mon Sep 17 00:00:00 2001 From: auberginewly <3127221787@qq.com> Date: Tue, 19 May 2026 10:48:44 +0800 Subject: [PATCH 1/4] feat(zoom): add hold-to-preview button for zoom focus editing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a zoom region is selected and paused, the editor shows the full un-zoomed frame for focus-point placement. This adds a press-and-hold "Preview" button so editors can momentarily see the zoomed result at the current focus + depth — like a before/after compare — without entering playback. - VideoPlayback: new transient isPreviewingZoom prop; shouldShowUnzoomedView now also requires !isPreviewingZoom, so the zoom transform is applied at the playhead while previewing - VideoEditor: isPreviewingZoom state wired to VideoPlayback and to onZoomPreviewStart/End handlers - SettingsPanel: hold button in the zoom controls (pointer down/up/leave/ cancel) - i18n: zoom.previewHold added across all 11 locales Prototype for #612 — placement (panel vs overlay) and hold-vs-toggle still open for maintainer direction. --- src/components/video-editor/SettingsPanel.tsx | 16 ++++++++++++++++ src/components/video-editor/VideoEditor.tsx | 4 ++++ src/components/video-editor/VideoPlayback.tsx | 12 +++++++++++- src/i18n/locales/ar/settings.json | 1 + src/i18n/locales/en/settings.json | 1 + src/i18n/locales/es/settings.json | 1 + src/i18n/locales/fr/settings.json | 1 + src/i18n/locales/ja-JP/settings.json | 1 + src/i18n/locales/ko-KR/settings.json | 1 + src/i18n/locales/ru/settings.json | 1 + src/i18n/locales/tr/settings.json | 1 + src/i18n/locales/vi/settings.json | 1 + src/i18n/locales/zh-CN/settings.json | 1 + src/i18n/locales/zh-TW/settings.json | 1 + 14 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/components/video-editor/SettingsPanel.tsx b/src/components/video-editor/SettingsPanel.tsx index 1001d4b7..1b37499c 100644 --- a/src/components/video-editor/SettingsPanel.tsx +++ b/src/components/video-editor/SettingsPanel.tsx @@ -230,6 +230,8 @@ interface SettingsPanelProps { selectedZoomCustomScale?: number | null; onZoomCustomScaleChange?: (scale: number) => void; onZoomCustomScaleCommit?: () => void; + onZoomPreviewStart?: () => void; + onZoomPreviewEnd?: () => void; selectedZoomFocusMode?: ZoomFocusMode | null; onZoomFocusModeChange?: (mode: ZoomFocusMode) => void; selectedZoomFocus?: ZoomFocus | null; @@ -359,6 +361,8 @@ export function SettingsPanel({ selectedZoomCustomScale, onZoomCustomScaleChange, onZoomCustomScaleCommit, + onZoomPreviewStart, + onZoomPreviewEnd, selectedZoomFocusMode, onZoomFocusModeChange, selectedZoomFocus, @@ -940,6 +944,18 @@ export function SettingsPanel({ )} + {zoomEnabled && (onZoomPreviewStart || onZoomPreviewEnd) && ( + + )} {zoomEnabled && selectedZoomFocusMode !== "auto" && selectedZoomFocus && diff --git a/src/components/video-editor/VideoEditor.tsx b/src/components/video-editor/VideoEditor.tsx index 15d582d3..818f663c 100644 --- a/src/components/video-editor/VideoEditor.tsx +++ b/src/components/video-editor/VideoEditor.tsx @@ -194,6 +194,7 @@ export default function VideoEditor() { const durationRef = useRef(duration); durationRef.current = duration; const [selectedZoomId, setSelectedZoomId] = useState(null); + const [isPreviewingZoom, setIsPreviewingZoom] = useState(false); const [selectedTrimId, setSelectedTrimId] = useState(null); const [selectedSpeedId, setSelectedSpeedId] = useState(null); const [selectedAnnotationId, setSelectedAnnotationId] = useState(null); @@ -2112,6 +2113,7 @@ export default function VideoEditor() { cursorMotionBlur={cursorMotionBlur} cursorClickBounce={cursorClickBounce} cursorClipToBounds={cursorClipToBounds} + isPreviewingZoom={isPreviewingZoom} /> @@ -2147,6 +2149,8 @@ export default function VideoEditor() { } onZoomCustomScaleChange={handleZoomCustomScaleChange} onZoomCustomScaleCommit={handleZoomCustomScaleCommit} + onZoomPreviewStart={() => setIsPreviewingZoom(true)} + onZoomPreviewEnd={() => setIsPreviewingZoom(false)} selectedZoomFocusMode={ selectedZoomId ? (zoomRegions.find((z) => z.id === selectedZoomId)?.focusMode ?? "manual") diff --git a/src/components/video-editor/VideoPlayback.tsx b/src/components/video-editor/VideoPlayback.tsx index 45ca9dfd..0c58572f 100644 --- a/src/components/video-editor/VideoPlayback.tsx +++ b/src/components/video-editor/VideoPlayback.tsx @@ -149,6 +149,9 @@ interface VideoPlaybackProps { cursorMotionBlur?: number; cursorClickBounce?: number; cursorClipToBounds?: boolean; + // When true, render the selected zoom at the playhead even while paused — + // lets the editor preview the zoom effect without leaving the focus-edit view. + isPreviewingZoom?: boolean; } export interface VideoPlaybackRef { @@ -270,6 +273,7 @@ const VideoPlayback = forwardRef( cursorMotionBlur = DEFAULT_CURSOR_MOTION_BLUR, cursorClickBounce = DEFAULT_CURSOR_CLICK_BOUNCE, cursorClipToBounds = false, + isPreviewingZoom = false, }, ref, ) => { @@ -341,6 +345,7 @@ const VideoPlayback = forwardRef( const cursorMotionBlurRef = useRef(cursorMotionBlur); const cursorClickBounceRef = useRef(cursorClickBounce); const cursorClipToBoundsRef = useRef(cursorClipToBounds); + const isPreviewingZoomRef = useRef(isPreviewingZoom); const motionBlurStateRef = useRef(createMotionBlurState()); const onTimeUpdateRef = useRef(onTimeUpdate); const onPlayStateChangeRef = useRef(onPlayStateChange); @@ -832,6 +837,10 @@ const VideoPlayback = forwardRef( cursorClipToBoundsRef.current = cursorClipToBounds; }, [cursorClipToBounds]); + useEffect(() => { + isPreviewingZoomRef.current = isPreviewingZoom; + }, [isPreviewingZoom]); + // Sync cursor overlay config when settings change useEffect(() => { const overlay = cursorOverlayRef.current; @@ -1309,7 +1318,8 @@ const VideoPlayback = forwardRef( // If a zoom is selected but video is not playing, show default unzoomed view const selectedId = selectedZoomIdRef.current; const hasSelectedZoom = selectedId !== null; - const shouldShowUnzoomedView = hasSelectedZoom && !isPlayingRef.current; + const shouldShowUnzoomedView = + hasSelectedZoom && !isPlayingRef.current && !isPreviewingZoomRef.current; if (region && strength > 0 && !shouldShowUnzoomedView) { const zoomScale = blendedScale ?? ZOOM_DEPTH_SCALES[region.depth]; diff --git a/src/i18n/locales/ar/settings.json b/src/i18n/locales/ar/settings.json index 1b4907c1..45789272 100644 --- a/src/i18n/locales/ar/settings.json +++ b/src/i18n/locales/ar/settings.json @@ -1,5 +1,6 @@ { "zoom": { + "previewHold": "اضغط مع الاستمرار للمعاينة", "level": "مستوى التكبير", "selectRegion": "حدد منطقة التكبير للتعديل", "deleteZoom": "حذف التكبير", diff --git a/src/i18n/locales/en/settings.json b/src/i18n/locales/en/settings.json index 9877d211..b5fc2db7 100644 --- a/src/i18n/locales/en/settings.json +++ b/src/i18n/locales/en/settings.json @@ -1,5 +1,6 @@ { "zoom": { + "previewHold": "Hold to preview", "level": "Zoom Level", "customScale": "Custom Zoom", "selectRegion": "Select a zoom region to adjust", diff --git a/src/i18n/locales/es/settings.json b/src/i18n/locales/es/settings.json index d03c0c72..6401db8a 100644 --- a/src/i18n/locales/es/settings.json +++ b/src/i18n/locales/es/settings.json @@ -1,5 +1,6 @@ { "zoom": { + "previewHold": "Mantener para previsualizar", "level": "Nivel de zoom", "selectRegion": "Selecciona una región de zoom para ajustar", "deleteZoom": "Eliminar zoom", diff --git a/src/i18n/locales/fr/settings.json b/src/i18n/locales/fr/settings.json index 8a53d44f..5af86913 100644 --- a/src/i18n/locales/fr/settings.json +++ b/src/i18n/locales/fr/settings.json @@ -1,5 +1,6 @@ { "zoom": { + "previewHold": "Maintenir pour prévisualiser", "level": "Niveau de zoom", "selectRegion": "Sélectionnez une région de zoom à ajuster", "deleteZoom": "Supprimer le zoom", diff --git a/src/i18n/locales/ja-JP/settings.json b/src/i18n/locales/ja-JP/settings.json index e7e15fab..5e2ee8b5 100644 --- a/src/i18n/locales/ja-JP/settings.json +++ b/src/i18n/locales/ja-JP/settings.json @@ -1,5 +1,6 @@ { "zoom": { + "previewHold": "押している間プレビュー", "level": "ズーム倍率", "selectRegion": "ズーム範囲を選択して調整", "deleteZoom": "ズームを削除", diff --git a/src/i18n/locales/ko-KR/settings.json b/src/i18n/locales/ko-KR/settings.json index c56df1dc..5e0c3769 100644 --- a/src/i18n/locales/ko-KR/settings.json +++ b/src/i18n/locales/ko-KR/settings.json @@ -1,5 +1,6 @@ { "zoom": { + "previewHold": "누르고 있으면 미리보기", "level": "줌 레벨", "selectRegion": "조정할 줌 구간을 선택하세요", "deleteZoom": "줌 삭제", diff --git a/src/i18n/locales/ru/settings.json b/src/i18n/locales/ru/settings.json index 3e77999c..f781ce5b 100644 --- a/src/i18n/locales/ru/settings.json +++ b/src/i18n/locales/ru/settings.json @@ -1,5 +1,6 @@ { "zoom": { + "previewHold": "Удерживайте для предпросмотра", "level": "Уровень масштабирования", "selectRegion": "Выберите область масштабирования для настройки", "deleteZoom": "Удалить масштабирование", diff --git a/src/i18n/locales/tr/settings.json b/src/i18n/locales/tr/settings.json index 8d998c30..a5fb9f67 100644 --- a/src/i18n/locales/tr/settings.json +++ b/src/i18n/locales/tr/settings.json @@ -1,5 +1,6 @@ { "zoom": { + "previewHold": "Önizlemek için basılı tutun", "level": "Yakınlaştırma Seviyesi", "selectRegion": "Ayarlamak için bir yakınlaştırma bölgesi seçin", "deleteZoom": "Yakınlaştırmayı Sil", diff --git a/src/i18n/locales/vi/settings.json b/src/i18n/locales/vi/settings.json index 84497621..76e27087 100644 --- a/src/i18n/locales/vi/settings.json +++ b/src/i18n/locales/vi/settings.json @@ -1,5 +1,6 @@ { "zoom": { + "previewHold": "Giữ để xem trước", "level": "Mức độ thu phóng", "selectRegion": "Chọn vùng thu phóng để điều chỉnh", "deleteZoom": "Xóa thu phóng", diff --git a/src/i18n/locales/zh-CN/settings.json b/src/i18n/locales/zh-CN/settings.json index 62dcae7c..4c5ea2da 100644 --- a/src/i18n/locales/zh-CN/settings.json +++ b/src/i18n/locales/zh-CN/settings.json @@ -1,5 +1,6 @@ { "zoom": { + "previewHold": "按住预览", "level": "缩放级别", "selectRegion": "选择要调整的缩放区域", "deleteZoom": "删除缩放", diff --git a/src/i18n/locales/zh-TW/settings.json b/src/i18n/locales/zh-TW/settings.json index 100c7564..decda1bc 100644 --- a/src/i18n/locales/zh-TW/settings.json +++ b/src/i18n/locales/zh-TW/settings.json @@ -1,5 +1,6 @@ { "zoom": { + "previewHold": "按住預覽", "level": "縮放級別", "selectRegion": "選擇要調整的縮放區域", "deleteZoom": "刪除縮放", From 24ce67b5a7297efb360b73cd3e42571682e06ae8 Mon Sep 17 00:00:00 2001 From: auberginewly <3127221787@qq.com> Date: Tue, 19 May 2026 10:51:22 +0800 Subject: [PATCH 2/4] i18n(zoom): clarify previewHold wording to "preview zoom effect" Make the label explicit about what holding previews (the zoom effect), across all 11 locales. --- src/i18n/locales/ar/settings.json | 2 +- src/i18n/locales/en/settings.json | 2 +- src/i18n/locales/es/settings.json | 2 +- src/i18n/locales/fr/settings.json | 2 +- src/i18n/locales/ja-JP/settings.json | 2 +- src/i18n/locales/ko-KR/settings.json | 2 +- src/i18n/locales/ru/settings.json | 2 +- src/i18n/locales/tr/settings.json | 2 +- src/i18n/locales/vi/settings.json | 2 +- src/i18n/locales/zh-CN/settings.json | 2 +- src/i18n/locales/zh-TW/settings.json | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/i18n/locales/ar/settings.json b/src/i18n/locales/ar/settings.json index 45789272..76729d84 100644 --- a/src/i18n/locales/ar/settings.json +++ b/src/i18n/locales/ar/settings.json @@ -1,6 +1,6 @@ { "zoom": { - "previewHold": "اضغط مع الاستمرار للمعاينة", + "previewHold": "اضغط مع الاستمرار لمعاينة تأثير التكبير", "level": "مستوى التكبير", "selectRegion": "حدد منطقة التكبير للتعديل", "deleteZoom": "حذف التكبير", diff --git a/src/i18n/locales/en/settings.json b/src/i18n/locales/en/settings.json index b5fc2db7..477b4598 100644 --- a/src/i18n/locales/en/settings.json +++ b/src/i18n/locales/en/settings.json @@ -1,6 +1,6 @@ { "zoom": { - "previewHold": "Hold to preview", + "previewHold": "Hold to preview zoom effect", "level": "Zoom Level", "customScale": "Custom Zoom", "selectRegion": "Select a zoom region to adjust", diff --git a/src/i18n/locales/es/settings.json b/src/i18n/locales/es/settings.json index 6401db8a..09fabad4 100644 --- a/src/i18n/locales/es/settings.json +++ b/src/i18n/locales/es/settings.json @@ -1,6 +1,6 @@ { "zoom": { - "previewHold": "Mantener para previsualizar", + "previewHold": "Mantener para previsualizar el efecto de zoom", "level": "Nivel de zoom", "selectRegion": "Selecciona una región de zoom para ajustar", "deleteZoom": "Eliminar zoom", diff --git a/src/i18n/locales/fr/settings.json b/src/i18n/locales/fr/settings.json index 5af86913..6009765f 100644 --- a/src/i18n/locales/fr/settings.json +++ b/src/i18n/locales/fr/settings.json @@ -1,6 +1,6 @@ { "zoom": { - "previewHold": "Maintenir pour prévisualiser", + "previewHold": "Maintenir pour prévisualiser l’effet de zoom", "level": "Niveau de zoom", "selectRegion": "Sélectionnez une région de zoom à ajuster", "deleteZoom": "Supprimer le zoom", diff --git a/src/i18n/locales/ja-JP/settings.json b/src/i18n/locales/ja-JP/settings.json index 5e2ee8b5..f1a604c9 100644 --- a/src/i18n/locales/ja-JP/settings.json +++ b/src/i18n/locales/ja-JP/settings.json @@ -1,6 +1,6 @@ { "zoom": { - "previewHold": "押している間プレビュー", + "previewHold": "押している間ズーム効果をプレビュー", "level": "ズーム倍率", "selectRegion": "ズーム範囲を選択して調整", "deleteZoom": "ズームを削除", diff --git a/src/i18n/locales/ko-KR/settings.json b/src/i18n/locales/ko-KR/settings.json index 5e0c3769..86fde766 100644 --- a/src/i18n/locales/ko-KR/settings.json +++ b/src/i18n/locales/ko-KR/settings.json @@ -1,6 +1,6 @@ { "zoom": { - "previewHold": "누르고 있으면 미리보기", + "previewHold": "누르고 있으면 줌 효과 미리보기", "level": "줌 레벨", "selectRegion": "조정할 줌 구간을 선택하세요", "deleteZoom": "줌 삭제", diff --git a/src/i18n/locales/ru/settings.json b/src/i18n/locales/ru/settings.json index f781ce5b..1ca59865 100644 --- a/src/i18n/locales/ru/settings.json +++ b/src/i18n/locales/ru/settings.json @@ -1,6 +1,6 @@ { "zoom": { - "previewHold": "Удерживайте для предпросмотра", + "previewHold": "Удерживайте для предпросмотра эффекта зума", "level": "Уровень масштабирования", "selectRegion": "Выберите область масштабирования для настройки", "deleteZoom": "Удалить масштабирование", diff --git a/src/i18n/locales/tr/settings.json b/src/i18n/locales/tr/settings.json index a5fb9f67..5adc2420 100644 --- a/src/i18n/locales/tr/settings.json +++ b/src/i18n/locales/tr/settings.json @@ -1,6 +1,6 @@ { "zoom": { - "previewHold": "Önizlemek için basılı tutun", + "previewHold": "Yakınlaştırma efektini önizlemek için basılı tutun", "level": "Yakınlaştırma Seviyesi", "selectRegion": "Ayarlamak için bir yakınlaştırma bölgesi seçin", "deleteZoom": "Yakınlaştırmayı Sil", diff --git a/src/i18n/locales/vi/settings.json b/src/i18n/locales/vi/settings.json index 76e27087..ba690465 100644 --- a/src/i18n/locales/vi/settings.json +++ b/src/i18n/locales/vi/settings.json @@ -1,6 +1,6 @@ { "zoom": { - "previewHold": "Giữ để xem trước", + "previewHold": "Giữ để xem trước hiệu ứng phóng to", "level": "Mức độ thu phóng", "selectRegion": "Chọn vùng thu phóng để điều chỉnh", "deleteZoom": "Xóa thu phóng", diff --git a/src/i18n/locales/zh-CN/settings.json b/src/i18n/locales/zh-CN/settings.json index 4c5ea2da..642d78f5 100644 --- a/src/i18n/locales/zh-CN/settings.json +++ b/src/i18n/locales/zh-CN/settings.json @@ -1,6 +1,6 @@ { "zoom": { - "previewHold": "按住预览", + "previewHold": "按住预览放大效果", "level": "缩放级别", "selectRegion": "选择要调整的缩放区域", "deleteZoom": "删除缩放", diff --git a/src/i18n/locales/zh-TW/settings.json b/src/i18n/locales/zh-TW/settings.json index decda1bc..825169dc 100644 --- a/src/i18n/locales/zh-TW/settings.json +++ b/src/i18n/locales/zh-TW/settings.json @@ -1,6 +1,6 @@ { "zoom": { - "previewHold": "按住預覽", + "previewHold": "按住預覽放大效果", "level": "縮放級別", "selectRegion": "選擇要調整的縮放區域", "deleteZoom": "刪除縮放", From a686fa012a3c132e918bbdd5aece7411ee574696 Mon Sep 17 00:00:00 2001 From: auberginewly <3127221787@qq.com> Date: Tue, 19 May 2026 11:02:28 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix(zoom):=20address=20PR=20review=20?= =?UTF-8?q?=E2=80=94=20preview=20selected=20zoom=20+=20keyboard=20a11y?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - VideoPlayback: while holding Preview, render the SELECTED zoom at full strength regardless of the playhead, instead of whatever findDominantRegion returns at currentTime (which is none/another zoom when the playhead is outside the selection). Uses getZoomScale/getRotation3D for the region's configured scale and 3D preset. - SettingsPanel: require both onZoomPreviewStart && onZoomPreviewEnd to render the button (full lifecycle), and add keyboard support — Space/Enter keydown (repeat-guarded) starts preview, keyup/blur ends it. --- src/components/video-editor/SettingsPanel.tsx | 23 +++++++++++++++---- src/components/video-editor/VideoPlayback.tsx | 19 ++++++++++++++- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/components/video-editor/SettingsPanel.tsx b/src/components/video-editor/SettingsPanel.tsx index 1b37499c..383fc48c 100644 --- a/src/components/video-editor/SettingsPanel.tsx +++ b/src/components/video-editor/SettingsPanel.tsx @@ -944,13 +944,26 @@ export function SettingsPanel({ )} - {zoomEnabled && (onZoomPreviewStart || onZoomPreviewEnd) && ( + {zoomEnabled && onZoomPreviewStart && onZoomPreviewEnd && (