fix(phase1): replace all magic numbers with named constants
AI Code Review / AI Code Review (pull_request) Failing after 0s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 44s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 40s
CI/CD Pipeline / Frontend Lint (pull_request) Failing after 1m31s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m48s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m50s
Preview Deploy / Deploy Preview Environment (pull_request) Failing after 22s
CI/CD Pipeline / Frontend Unit Tests (pull_request) Failing after 24s
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 2m41s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m43s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 4m5s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m55s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 46m8s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 1252h10m29s
CI/CD Pipeline / Production Browser E2E (pull_request) Failing after 1252h12m12s
CI/CD Pipeline / ACR Image Cleanup (pull_request) Failing after 1252h12m13s
CI/CD Pipeline / Staging E2E Tests (pull_request) Failing after 1252h12m15s
CI/CD Pipeline / Deploy Production (pull_request) Failing after 1252h12m16s
CI/CD Pipeline / Build Production Worker Image (pull_request) Failing after 1252h12m20s
CI/CD Pipeline / Build Production Web Image (pull_request) Failing after 1252h12m22s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 1252h12m43s
CI/CD Pipeline / Build Staging Worker Image (pull_request) Failing after 1252h13m5s
CI/CD Pipeline / Build Staging API Image (pull_request) Failing after 1252h13m9s
CI/CD Pipeline / Staging API Integration Tests (pull_request) Failing after 1252h44m52s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Failing after 1252h44m56s
CI/CD Pipeline / Build Production API Image (pull_request) Failing after 1252h45m2s
CI/CD Pipeline / Canary Release to Production (pull_request) Failing after 1252h44m49s
CI/CD Pipeline / Build Staging Web Image (pull_request) Failing after 1252h45m45s

- Replace hardcoded values with imported constants
- Fix unused import errors (TS6133)
- MIN/MAX pixels per second, zoom step, clip width, trim duration, add duration, track width, picker width, track gap
This commit is contained in:
SaaS Frontend
2026-07-26 11:13:30 +08:00
parent c71352c2b7
commit c704f9b844
@@ -141,7 +141,7 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
/* ── "+" 卡片:类型+时长选择状态 ── */
const [addType, setAddType] = useState<ClipType>(defaultAddType)
const [addDuration, setAddDuration] = useState<number>(5)
const [addDuration, setAddDuration] = useState<number>(DEFAULT_ADD_DURATION)
/* ── 模式切换时自动同步默认添加类型 ── */
useEffect(() => {
@@ -150,21 +150,17 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
}
}, [currentMode, addType, availableTypes, defaultAddType])
/* ── 面板尺寸 ── */
const PICKER_W = 240
const GAP = 6
/* ── 计算 picker 初始位置 ── */
const updatePickerPosition = useCallback(() => {
if (!addCardRef.current) return
const rect = addCardRef.current.getBoundingClientRect()
const vw = window.innerWidth
const roughHeight = 180
let top = rect.top - GAP - roughHeight
let top = rect.top - TRACK_GAP - roughHeight
if (top < 8) top = 8
let right = vw - rect.right
if (rect.right - PICKER_W < 8) {
right = vw - PICKER_W - 8
if (rect.right - ADD_PICKER_WIDTH < 8) {
right = vw - ADD_PICKER_WIDTH - 8
}
setPickerPos({ top, right })
}, [])
@@ -187,9 +183,9 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
const pickerH = pickerEl.offsetHeight
const vh = window.innerHeight
const vw = window.innerWidth
let top = addRect.top - GAP - pickerH
let top = addRect.top - TRACK_GAP - pickerH
if (top < 8) {
top = addRect.bottom + GAP
top = addRect.bottom + TRACK_GAP
if (top + pickerH > vh - 8) {
top = vh - 8 - pickerH
if (top < 8) top = 8
@@ -198,7 +194,7 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
let right = vw - addRect.right
const pickerRect = pickerEl.getBoundingClientRect()
if (pickerRect.left < 8) {
right = vw - PICKER_W - 8
right = vw - ADD_PICKER_WIDTH - 8
}
setPickerPos({ top, right })
}, [showAddPicker])
@@ -230,7 +226,7 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
}, [contextMenu])
/* ── 缩放 & 时长 ── */
const pps = pixelsPerSecond ?? 40
const pps = pixelsPerSecond ?? DEFAULT_PIXELS_PER_SECOND
const totalDuration = totalDurationProp ?? clips.reduce((s, c) => s + c.duration, 0)
/* ── 播放头拖拽全局 mousemove/mouseup ── */
@@ -350,11 +346,9 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
useEffect(() => {
if (!trimDrag) return
const PX_PER_SECOND = pixelsPerSecond ?? 40 // 与缩放级别同步
const handleMouseMove = (e: MouseEvent) => {
const dx = e.clientX - trimDrag.startX
const dtSec = dx / PX_PER_SECOND
const dtSec = dx / pps
const clip = clips.find((c) => c.id === trimDrag.clipId)
if (!clip) return
@@ -365,10 +359,10 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
if (trimDrag.direction === "left") {
// 左手柄:调整入点
newStart = Math.max(0, Math.min(origTrim.start_time + dtSec, newEnd - 1))
newStart = Math.max(0, Math.min(origTrim.start_time + dtSec, newEnd - MIN_TRIM_DURATION))
} else {
// 右手柄:调整出点
newEnd = Math.max(origTrim.start_time + 1, Math.min(origTrim.end_time + dtSec, origDur))
newEnd = Math.max(origTrim.start_time + MIN_TRIM_DURATION, Math.min(origTrim.end_time + dtSec, origDur))
}
const newDuration = Math.round((newEnd - newStart) * 10) / 10
@@ -435,7 +429,7 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
}, [contextMenu, onClipRemove])
/* ── 时间标尺 ── */
const trackWidth = Math.max(totalDuration * pps, 300)
const trackWidth = Math.max(totalDuration * pps, MIN_TRACK_WIDTH)
const step = getRulerStep(totalDuration)
const rulerMarks = generateRulerMarks(totalDuration, step)
@@ -452,7 +446,7 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
<div className="ep-timeline-zoom">
<button
className="ep-zoom-btn"
onClick={() => onZoomChange?.(Math.max(10, pps - 10))}
onClick={() => onZoomChange?.(Math.max(MIN_PIXELS_PER_SECOND, pps - ZOOM_STEP))}
title="缩小"
>
−
@@ -460,15 +454,15 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
<input
type="range"
className="ep-zoom-slider"
min={10}
max={120}
min={MIN_PIXELS_PER_SECOND}
max={MAX_PIXELS_PER_SECOND}
step={5}
value={pps}
onChange={(e) => onZoomChange?.(Number(e.target.value))}
/>
<button
className="ep-zoom-btn"
onClick={() => onZoomChange?.(Math.min(120, pps + 10))}
onClick={() => onZoomChange?.(Math.min(MAX_PIXELS_PER_SECOND, pps + ZOOM_STEP))}
title="放大"
>
+
@@ -559,7 +553,7 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
<div
className={`ep-clip-card ${selectedClipId === clip.id ? "selected" : ""} ${dragIdx === idx ? "dragging" : ""} ${dragOverIdx === idx ? "drag-over" : ""} ${hasTrim ? "trimmed" : ""}`}
style={{ width: Math.max(clip.duration * pps, 60) }}
style={{ width: Math.max(clip.duration * pps, MIN_CLIP_WIDTH) }}
draggable={!trimDrag}
onDragStart={(e) => handleDragStart(e, idx)}
onDragOver={(e) => handleDragOver(e, idx)}
@@ -743,11 +737,11 @@ const TimelinePanel: React.FC<TimelinePanelProps> = ({
<input
type="number"
className="ep-duration-input"
min={1}
max={120}
min={MIN_ADD_DURATION}
max={MAX_ADD_DURATION}
value={addDuration}
onChange={(e) =>
setAddDuration(Math.max(1, Math.min(120, Number(e.target.value) || 1)))
setAddDuration(Math.max(MIN_ADD_DURATION, Math.min(MAX_ADD_DURATION, Number(e.target.value) || MIN_ADD_DURATION)))
}
/>
<span className="ep-add-clip-duration-unit">秒</span>