fix: prettier format
CI/CD Pipeline / Check if frontend-only change (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Lint (pull_request) Has been cancelled
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / PR Build API Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Web Image (pull_request) Has been cancelled
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been cancelled
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled

This commit is contained in:
2026-07-30 09:28:42 +08:00
parent 30666caa44
commit 8835e2d77a
@@ -17,44 +17,48 @@
* - useTimelineMenus - 菜单 & 面板
* - usePlayheadDrag - 播放头拖拽
*/
import React from "react"
import type { ClipData, ClipType, TrimConfig } from "../types"
import { DEFAULT_PIXELS_PER_SECOND } from "../constants/timeline"
import { useClipDrag } from "../hooks/useClipDrag"
import { useTrimDrag } from "../hooks/useTrimDrag"
import { useTimelineMenus } from "../hooks/useTimelineMenus"
import { usePlayheadDrag } from "./timeline/hooks/usePlayheadDrag"
import { TimeRuler } from "./timeline/TimeRuler"
import { ClipTrack } from "./timeline/ClipTrack"
import { TimelineHeader } from "./timeline/TimelineHeader"
import { AddClipPicker } from "./timeline/AddClipPicker"
import { TrimPreview } from "./timeline/TrimPreview"
import { ContextMenu } from "./timeline/ContextMenu"
import React from "react";
import type { ClipData, ClipType, TrimConfig } from "../types";
import { DEFAULT_PIXELS_PER_SECOND } from "../constants/timeline";
import { useClipDrag } from "../hooks/useClipDrag";
import { useTrimDrag } from "../hooks/useTrimDrag";
import { useTimelineMenus } from "../hooks/useTimelineMenus";
import { usePlayheadDrag } from "./timeline/hooks/usePlayheadDrag";
import { TimeRuler } from "./timeline/TimeRuler";
import { ClipTrack } from "./timeline/ClipTrack";
import { TimelineHeader } from "./timeline/TimelineHeader";
import { AddClipPicker } from "./timeline/AddClipPicker";
import { TrimPreview } from "./timeline/TrimPreview";
import { ContextMenu } from "./timeline/ContextMenu";
interface TimelinePanelProps {
clips: ClipData[]
selectedClipId: string | null
currentMode: string
onClipSelect: (clipId: string) => void
onClipReorder: (fromIdx: number, toIdx: number) => void
onClipRemove: (clipId: string) => void
onAddClip: (type: ClipType, duration: number) => void
clips: ClipData[];
selectedClipId: string | null;
currentMode: string;
onClipSelect: (clipId: string) => void;
onClipReorder: (fromIdx: number, toIdx: number) => void;
onClipRemove: (clipId: string) => void;
onAddClip: (type: ClipType, duration: number) => void;
/** 裁剪更新:调整片段的 trim_config 和 duration */
onClipTrim?: (clipId: string, trimConfig: TrimConfig, newDuration: number) => void
onClipTrim?: (
clipId: string,
trimConfig: TrimConfig,
newDuration: number,
) => void;
/** 在指定位置分割片段 */
onClipSplit?: (clipId: string, splitRatio: number) => void
onClipSplit?: (clipId: string, splitRatio: number) => void;
/** 恢复片段原始长度 */
onClipResetTrim?: (clipId: string) => void
onClipResetTrim?: (clipId: string) => void;
/** 当前播放时间(秒) */
currentTime?: number
currentTime?: number;
/** 缩放:每秒像素数 */
pixelsPerSecond?: number
pixelsPerSecond?: number;
/** 缩放变更回调 */
onZoomChange?: (pps: number) => void
onZoomChange?: (pps: number) => void;
/** 播放头跳转回调 */
onSeek?: (time: number) => void
onSeek?: (time: number) => void;
/** 总时长(秒),可选(默认由 clips 计算) */
totalDuration?: number
totalDuration?: number;
}
const TimelinePanel: React.FC<TimelinePanelProps> = ({
@@ -75,11 +79,16 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
totalDuration: totalDurationProp,
}) => {
/* ── 缩放 & 时长 ── */
const pps = pixelsPerSecond ?? DEFAULT_PIXELS_PER_SECOND
const totalDuration = totalDurationProp ?? clips.reduce((s, c) => s + c.duration, 0)
const pps = pixelsPerSecond ?? DEFAULT_PIXELS_PER_SECOND;
const totalDuration =
totalDurationProp ?? clips.reduce((s, c) => s + c.duration, 0);
/* ── 裁剪拖拽 ── */
const { trimDrag, trimPreview, handleTrimHandleMouseDown } = useTrimDrag(clips, pps, onClipTrim)
const { trimDrag, trimPreview, handleTrimHandleMouseDown } = useTrimDrag(
clips,
pps,
onClipTrim,
);
/* ── 片段拖拽排序 ── */
const {
@@ -90,7 +99,7 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
handleDragEnd,
handleDrop,
handleEmptyDragOver,
} = useClipDrag(onClipReorder, !!trimDrag)
} = useClipDrag(onClipReorder, !!trimDrag);
/* ── 菜单 & 面板 ── */
const {
@@ -113,23 +122,31 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
handleConfirmAdd,
hoveredClipId,
setHoveredClipId,
} = useTimelineMenus(clips, currentMode, onAddClip, onClipSplit, onClipResetTrim, onClipRemove)
} = useTimelineMenus(
clips,
currentMode,
onAddClip,
onClipSplit,
onClipResetTrim,
onClipRemove,
);
/* ── 播放头拖拽 ── */
const { trackRef, handlePlayheadMouseDown, handleRulerClick } = usePlayheadDrag({
currentTime,
pps,
totalDuration,
onSeek,
})
const { trackRef, handlePlayheadMouseDown, handleRulerClick } =
usePlayheadDrag({
currentTime,
pps,
totalDuration,
onSeek,
});
/* ── 撤销最后一个片段 ── */
const handleUndoClip = () => {
if (clips.length > 1) {
const last = clips[clips.length - 1]
onClipRemove(last.id)
const last = clips[clips.length - 1];
onClipRemove(last.id);
}
}
};
return (
<div className="ep-timeline-area">
@@ -143,11 +160,17 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
/>
{/* 一镜到底提示 */}
{currentMode === "one_take" && <div className="ep-one-take-hint">🎥 </div>}
{currentMode === "one_take" && (
<div className="ep-one-take-hint">🎥 </div>
)}
{/* 时间标尺 */}
{currentMode !== "one_take" && (
<TimeRuler totalDuration={totalDuration} pps={pps} onClick={handleRulerClick} />
<TimeRuler
totalDuration={totalDuration}
pps={pps}
onClick={handleRulerClick}
/>
)}
{/* 水平片段轨道 */}
@@ -198,7 +221,9 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
x={contextMenu.x}
y={contextMenu.y}
menuRef={contextMenuRef}
hasTrim={!!clips.find((c) => c.id === contextMenu.clipId)?.trim_config}
hasTrim={
!!clips.find((c) => c.id === contextMenu.clipId)?.trim_config
}
onSplit={handleContextSplit}
onResetTrim={handleContextResetTrim}
onDelete={handleContextDelete}
@@ -219,7 +244,7 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
/>
)}
</div>
)
}
);
};
export default TimelinePanel
export default TimelinePanel;