lyrics-transcriber 0.68.0__py3-none-any.whl → 0.69.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lyrics_transcriber/frontend/REPLACE_ALL_FUNCTIONALITY.md +210 -0
- lyrics_transcriber/frontend/package.json +1 -1
- lyrics_transcriber/frontend/src/components/AudioPlayer.tsx +4 -2
- lyrics_transcriber/frontend/src/components/EditTimelineSection.tsx +257 -134
- lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +35 -237
- lyrics_transcriber/frontend/src/components/ReferenceView.tsx +27 -3
- lyrics_transcriber/frontend/src/components/ReplaceAllLyricsModal.tsx +688 -0
- lyrics_transcriber/frontend/src/components/TimelineEditor.tsx +29 -18
- lyrics_transcriber/frontend/src/hooks/useManualSync.ts +198 -30
- lyrics_transcriber/frontend/tsconfig.tsbuildinfo +1 -1
- lyrics_transcriber/frontend/web_assets/assets/{index-D7BQUJXK.js → index-izP9z1oB.js} +985 -327
- lyrics_transcriber/frontend/web_assets/assets/index-izP9z1oB.js.map +1 -0
- lyrics_transcriber/frontend/web_assets/index.html +1 -1
- {lyrics_transcriber-0.68.0.dist-info → lyrics_transcriber-0.69.0.dist-info}/METADATA +1 -1
- {lyrics_transcriber-0.68.0.dist-info → lyrics_transcriber-0.69.0.dist-info}/RECORD +18 -16
- lyrics_transcriber/frontend/web_assets/assets/index-D7BQUJXK.js.map +0 -1
- {lyrics_transcriber-0.68.0.dist-info → lyrics_transcriber-0.69.0.dist-info}/LICENSE +0 -0
- {lyrics_transcriber-0.68.0.dist-info → lyrics_transcriber-0.69.0.dist-info}/WHEEL +0 -0
- {lyrics_transcriber-0.68.0.dist-info → lyrics_transcriber-0.69.0.dist-info}/entry_points.txt +0 -0
@@ -14,10 +14,12 @@ import ArrowBackIcon from '@mui/icons-material/ArrowBack'
|
|
14
14
|
import ArrowForwardIcon from '@mui/icons-material/ArrowForward'
|
15
15
|
import AutorenewIcon from '@mui/icons-material/Autorenew'
|
16
16
|
import PauseCircleOutlineIcon from '@mui/icons-material/PauseCircleOutline'
|
17
|
+
import PlayArrowIcon from '@mui/icons-material/PlayArrow'
|
18
|
+
import StopIcon from '@mui/icons-material/Stop'
|
17
19
|
import CenterFocusStrongIcon from '@mui/icons-material/CenterFocusStrong'
|
18
20
|
import TimelineEditor from './TimelineEditor'
|
19
21
|
import { Word } from '../types'
|
20
|
-
import { useState, useEffect, useCallback, useRef } from 'react'
|
22
|
+
import { useState, useEffect, useCallback, useRef, useMemo, memo } from 'react'
|
21
23
|
|
22
24
|
interface EditTimelineSectionProps {
|
23
25
|
words: Word[]
|
@@ -32,11 +34,160 @@ interface EditTimelineSectionProps {
|
|
32
34
|
syncWordIndex: number
|
33
35
|
isSpacebarPressed: boolean
|
34
36
|
onWordUpdate: (index: number, updates: Partial<Word>) => void
|
37
|
+
onUnsyncWord?: (index: number) => void
|
35
38
|
onPlaySegment?: (time: number) => void
|
39
|
+
onStopAudio?: () => void
|
36
40
|
startManualSync: () => void
|
41
|
+
pauseManualSync?: () => void
|
42
|
+
resumeManualSync?: () => void
|
43
|
+
isPaused?: boolean
|
37
44
|
isGlobal?: boolean
|
45
|
+
defaultZoomLevel?: number
|
46
|
+
isReplaceAllMode?: boolean
|
38
47
|
}
|
39
48
|
|
49
|
+
// Memoized control buttons to prevent unnecessary re-renders
|
50
|
+
const TimelineControls = memo(({
|
51
|
+
isGlobal,
|
52
|
+
visibleStartTime,
|
53
|
+
visibleEndTime,
|
54
|
+
startTime,
|
55
|
+
endTime,
|
56
|
+
zoomLevel,
|
57
|
+
autoScrollEnabled,
|
58
|
+
currentTime,
|
59
|
+
isManualSyncing,
|
60
|
+
isReplaceAllMode,
|
61
|
+
isPaused,
|
62
|
+
onScrollLeft,
|
63
|
+
onZoomOut,
|
64
|
+
onZoomIn,
|
65
|
+
onScrollRight,
|
66
|
+
onToggleAutoScroll,
|
67
|
+
onJumpToCurrentTime,
|
68
|
+
onStartManualSync,
|
69
|
+
onPauseResume,
|
70
|
+
onStopAudio
|
71
|
+
}: {
|
72
|
+
isGlobal: boolean
|
73
|
+
visibleStartTime: number
|
74
|
+
visibleEndTime: number
|
75
|
+
startTime: number
|
76
|
+
endTime: number
|
77
|
+
zoomLevel: number
|
78
|
+
autoScrollEnabled: boolean
|
79
|
+
currentTime?: number
|
80
|
+
isManualSyncing: boolean
|
81
|
+
isReplaceAllMode: boolean
|
82
|
+
isPaused: boolean
|
83
|
+
onScrollLeft: () => void
|
84
|
+
onZoomOut: () => void
|
85
|
+
onZoomIn: () => void
|
86
|
+
onScrollRight: () => void
|
87
|
+
onToggleAutoScroll: () => void
|
88
|
+
onJumpToCurrentTime: () => void
|
89
|
+
onStartManualSync: () => void
|
90
|
+
onPauseResume: () => void
|
91
|
+
onStopAudio?: () => void
|
92
|
+
}) => {
|
93
|
+
return (
|
94
|
+
<Stack direction="row" spacing={1} alignItems="center">
|
95
|
+
{isGlobal && (
|
96
|
+
<>
|
97
|
+
<Tooltip title="Scroll Left">
|
98
|
+
<IconButton
|
99
|
+
onClick={onScrollLeft}
|
100
|
+
disabled={visibleStartTime <= startTime}
|
101
|
+
size="small"
|
102
|
+
>
|
103
|
+
<ArrowBackIcon />
|
104
|
+
</IconButton>
|
105
|
+
</Tooltip>
|
106
|
+
<Tooltip title="Zoom Out (Show More Time)">
|
107
|
+
<IconButton
|
108
|
+
onClick={onZoomOut}
|
109
|
+
disabled={zoomLevel >= (endTime - startTime) || (isReplaceAllMode && isManualSyncing && !isPaused)}
|
110
|
+
size="small"
|
111
|
+
>
|
112
|
+
<ZoomOutIcon />
|
113
|
+
</IconButton>
|
114
|
+
</Tooltip>
|
115
|
+
<Tooltip title="Zoom In (Show Less Time)">
|
116
|
+
<IconButton
|
117
|
+
onClick={onZoomIn}
|
118
|
+
disabled={zoomLevel <= 2 || (isReplaceAllMode && isManualSyncing && !isPaused)}
|
119
|
+
size="small"
|
120
|
+
>
|
121
|
+
<ZoomInIcon />
|
122
|
+
</IconButton>
|
123
|
+
</Tooltip>
|
124
|
+
<Tooltip title="Scroll Right">
|
125
|
+
<IconButton
|
126
|
+
onClick={onScrollRight}
|
127
|
+
disabled={visibleEndTime >= endTime}
|
128
|
+
size="small"
|
129
|
+
>
|
130
|
+
<ArrowForwardIcon />
|
131
|
+
</IconButton>
|
132
|
+
</Tooltip>
|
133
|
+
<Tooltip
|
134
|
+
title={autoScrollEnabled ?
|
135
|
+
"Disable Auto-Page Turn During Playback" :
|
136
|
+
"Enable Auto-Page Turn During Playback"}
|
137
|
+
>
|
138
|
+
<IconButton
|
139
|
+
onClick={onToggleAutoScroll}
|
140
|
+
color={autoScrollEnabled ? "primary" : "default"}
|
141
|
+
size="small"
|
142
|
+
>
|
143
|
+
{autoScrollEnabled ? <AutorenewIcon /> : <PauseCircleOutlineIcon />}
|
144
|
+
</IconButton>
|
145
|
+
</Tooltip>
|
146
|
+
<Tooltip title="Jump to Current Playback Position">
|
147
|
+
<IconButton
|
148
|
+
onClick={onJumpToCurrentTime}
|
149
|
+
disabled={!currentTime}
|
150
|
+
size="small"
|
151
|
+
>
|
152
|
+
<CenterFocusStrongIcon />
|
153
|
+
</IconButton>
|
154
|
+
</Tooltip>
|
155
|
+
</>
|
156
|
+
)}
|
157
|
+
{isReplaceAllMode && onStopAudio && (
|
158
|
+
<Button
|
159
|
+
variant="outlined"
|
160
|
+
onClick={onStopAudio}
|
161
|
+
startIcon={<StopIcon />}
|
162
|
+
color="error"
|
163
|
+
size="small"
|
164
|
+
>
|
165
|
+
Stop Audio
|
166
|
+
</Button>
|
167
|
+
)}
|
168
|
+
<Button
|
169
|
+
variant={isManualSyncing ? "outlined" : "contained"}
|
170
|
+
onClick={onStartManualSync}
|
171
|
+
startIcon={isManualSyncing ? <CancelIcon /> : <PlayCircleOutlineIcon />}
|
172
|
+
color={isManualSyncing ? "error" : "primary"}
|
173
|
+
>
|
174
|
+
{isManualSyncing ? "Cancel Sync" : "Manual Sync"}
|
175
|
+
</Button>
|
176
|
+
{isManualSyncing && isReplaceAllMode && (
|
177
|
+
<Button
|
178
|
+
variant="outlined"
|
179
|
+
onClick={onPauseResume}
|
180
|
+
startIcon={isPaused ? <PlayArrowIcon /> : <PauseCircleOutlineIcon />}
|
181
|
+
color={isPaused ? "success" : "warning"}
|
182
|
+
size="small"
|
183
|
+
>
|
184
|
+
{isPaused ? "Resume" : "Pause"}
|
185
|
+
</Button>
|
186
|
+
)}
|
187
|
+
</Stack>
|
188
|
+
)
|
189
|
+
})
|
190
|
+
|
40
191
|
export default function EditTimelineSection({
|
41
192
|
words,
|
42
193
|
startTime,
|
@@ -50,17 +201,38 @@ export default function EditTimelineSection({
|
|
50
201
|
syncWordIndex,
|
51
202
|
isSpacebarPressed,
|
52
203
|
onWordUpdate,
|
204
|
+
onUnsyncWord,
|
53
205
|
onPlaySegment,
|
206
|
+
onStopAudio,
|
54
207
|
startManualSync,
|
55
|
-
|
208
|
+
pauseManualSync,
|
209
|
+
resumeManualSync,
|
210
|
+
isPaused = false,
|
211
|
+
isGlobal = false,
|
212
|
+
defaultZoomLevel = 10,
|
213
|
+
isReplaceAllMode = false
|
56
214
|
}: EditTimelineSectionProps) {
|
57
|
-
// Add state for zoom level
|
58
|
-
const [zoomLevel, setZoomLevel] = useState(
|
215
|
+
// Add state for zoom level - use larger default for Replace All mode
|
216
|
+
const [zoomLevel, setZoomLevel] = useState(defaultZoomLevel)
|
59
217
|
const [visibleStartTime, setVisibleStartTime] = useState(startTime)
|
60
218
|
const [visibleEndTime, setVisibleEndTime] = useState(Math.min(startTime + zoomLevel, endTime))
|
61
219
|
const [autoScrollEnabled, setAutoScrollEnabled] = useState(true) // Default to enabled
|
62
220
|
const timelineRef = useRef<HTMLDivElement>(null)
|
63
221
|
|
222
|
+
// Memoize the effective time range to prevent recalculation
|
223
|
+
const effectiveTimeRange = useMemo(() => ({
|
224
|
+
start: isGlobal ? visibleStartTime : startTime,
|
225
|
+
end: isGlobal ? visibleEndTime : endTime
|
226
|
+
}), [isGlobal, visibleStartTime, visibleEndTime, startTime, endTime])
|
227
|
+
|
228
|
+
// Auto-enable auto-scroll when manual sync starts or resumes
|
229
|
+
useEffect(() => {
|
230
|
+
if (isManualSyncing && !isPaused) {
|
231
|
+
console.log('EditTimelineSection - Auto-enabling auto-scroll for manual sync')
|
232
|
+
setAutoScrollEnabled(true)
|
233
|
+
}
|
234
|
+
}, [isManualSyncing, isPaused])
|
235
|
+
|
64
236
|
// Initial setup of visible time range
|
65
237
|
useEffect(() => {
|
66
238
|
if (isGlobal) {
|
@@ -74,11 +246,20 @@ export default function EditTimelineSection({
|
|
74
246
|
}
|
75
247
|
}, [startTime, endTime, zoomLevel, isGlobal])
|
76
248
|
|
77
|
-
//
|
249
|
+
// Throttled auto-scroll to reduce frequent updates during playback
|
250
|
+
const lastScrollUpdateRef = useRef<number>(0)
|
251
|
+
const SCROLL_THROTTLE_MS = 100 // Only update scroll position every 100ms
|
252
|
+
|
253
|
+
// Handle playback scrolling with "page turning" approach - throttled for performance
|
78
254
|
useEffect(() => {
|
79
255
|
// Skip if not in global mode, no current time, or auto-scroll is disabled
|
80
256
|
if (!isGlobal || !currentTime || !autoScrollEnabled) return
|
81
257
|
|
258
|
+
// Throttle scroll updates for performance
|
259
|
+
const now = Date.now()
|
260
|
+
if (now - lastScrollUpdateRef.current < SCROLL_THROTTLE_MS) return
|
261
|
+
lastScrollUpdateRef.current = now
|
262
|
+
|
82
263
|
// Only scroll when current time is outside or near the edge of the visible window
|
83
264
|
if (currentTime < visibleStartTime) {
|
84
265
|
// If current time is before visible window, jump to show it at the start
|
@@ -122,12 +303,25 @@ export default function EditTimelineSection({
|
|
122
303
|
}
|
123
304
|
}, [zoomLevel, startTime, endTime, isGlobal, visibleStartTime])
|
124
305
|
|
125
|
-
//
|
126
|
-
const
|
306
|
+
// Memoized event handlers to prevent unnecessary re-renders
|
307
|
+
const handleZoomIn = useCallback(() => {
|
308
|
+
if (isReplaceAllMode && isManualSyncing && !isPaused) return // Prevent zoom changes during active sync (but allow when paused)
|
309
|
+
if (zoomLevel > 2) { // Minimum zoom level of 2 seconds
|
310
|
+
setZoomLevel(zoomLevel - 2)
|
311
|
+
}
|
312
|
+
}, [isReplaceAllMode, isManualSyncing, isPaused, zoomLevel])
|
313
|
+
|
314
|
+
const handleZoomOut = useCallback(() => {
|
315
|
+
if (isReplaceAllMode && isManualSyncing && !isPaused) return // Prevent zoom changes during active sync (but allow when paused)
|
316
|
+
if (zoomLevel < (endTime - startTime)) { // Maximum zoom is the full range
|
317
|
+
setZoomLevel(zoomLevel + 2)
|
318
|
+
}
|
319
|
+
}, [isReplaceAllMode, isManualSyncing, isPaused, zoomLevel, endTime, startTime])
|
320
|
+
|
321
|
+
const toggleAutoScroll = useCallback(() => {
|
127
322
|
setAutoScrollEnabled(!autoScrollEnabled)
|
128
|
-
}
|
323
|
+
}, [autoScrollEnabled])
|
129
324
|
|
130
|
-
// Jump to current playback position
|
131
325
|
const jumpToCurrentTime = useCallback(() => {
|
132
326
|
if (!isGlobal || !currentTime) return
|
133
327
|
|
@@ -145,45 +339,7 @@ export default function EditTimelineSection({
|
|
145
339
|
setVisibleEndTime(newEnd)
|
146
340
|
}, [currentTime, zoomLevel, startTime, endTime, isGlobal])
|
147
341
|
|
148
|
-
//
|
149
|
-
useEffect(() => {
|
150
|
-
const handleKeyDown = (e: KeyboardEvent) => {
|
151
|
-
if (isGlobal) {
|
152
|
-
// Alt+A to toggle auto-scroll
|
153
|
-
if (e.altKey && e.key === 'a') {
|
154
|
-
e.preventDefault()
|
155
|
-
toggleAutoScroll()
|
156
|
-
}
|
157
|
-
|
158
|
-
// Alt+J to jump to current time
|
159
|
-
if (e.altKey && e.key === 'j') {
|
160
|
-
e.preventDefault()
|
161
|
-
jumpToCurrentTime()
|
162
|
-
}
|
163
|
-
}
|
164
|
-
}
|
165
|
-
|
166
|
-
window.addEventListener('keydown', handleKeyDown)
|
167
|
-
return () => {
|
168
|
-
window.removeEventListener('keydown', handleKeyDown)
|
169
|
-
}
|
170
|
-
}, [isGlobal, toggleAutoScroll, jumpToCurrentTime])
|
171
|
-
|
172
|
-
// Handle zoom in
|
173
|
-
const handleZoomIn = () => {
|
174
|
-
if (zoomLevel > 2) { // Minimum zoom level of 2 seconds
|
175
|
-
setZoomLevel(zoomLevel - 2)
|
176
|
-
}
|
177
|
-
}
|
178
|
-
|
179
|
-
// Handle zoom out
|
180
|
-
const handleZoomOut = () => {
|
181
|
-
if (zoomLevel < (endTime - startTime)) { // Maximum zoom is the full range
|
182
|
-
setZoomLevel(zoomLevel + 2)
|
183
|
-
}
|
184
|
-
}
|
185
|
-
|
186
|
-
// Handle horizontal scrolling
|
342
|
+
// Handle horizontal scrolling - throttled for performance
|
187
343
|
const handleScroll = useCallback((event: React.WheelEvent<HTMLDivElement>) => {
|
188
344
|
if (isGlobal && event.deltaX !== 0) {
|
189
345
|
event.preventDefault()
|
@@ -214,8 +370,7 @@ export default function EditTimelineSection({
|
|
214
370
|
}
|
215
371
|
}, [isGlobal, visibleStartTime, visibleEndTime, startTime, endTime, zoomLevel])
|
216
372
|
|
217
|
-
|
218
|
-
const handleScrollLeft = () => {
|
373
|
+
const handleScrollLeft = useCallback(() => {
|
219
374
|
if (!isGlobal) return
|
220
375
|
|
221
376
|
// Disable auto-scroll when user manually scrolls
|
@@ -228,10 +383,9 @@ export default function EditTimelineSection({
|
|
228
383
|
|
229
384
|
setVisibleStartTime(newStart)
|
230
385
|
setVisibleEndTime(newEnd)
|
231
|
-
}
|
386
|
+
}, [isGlobal, zoomLevel, startTime, visibleStartTime])
|
232
387
|
|
233
|
-
|
234
|
-
const handleScrollRight = () => {
|
388
|
+
const handleScrollRight = useCallback(() => {
|
235
389
|
if (!isGlobal) return
|
236
390
|
|
237
391
|
// Disable auto-scroll when user manually scrolls
|
@@ -252,11 +406,28 @@ export default function EditTimelineSection({
|
|
252
406
|
}
|
253
407
|
|
254
408
|
setVisibleStartTime(newStart)
|
255
|
-
}
|
409
|
+
}, [isGlobal, zoomLevel, endTime, visibleEndTime, startTime])
|
256
410
|
|
257
|
-
|
258
|
-
|
259
|
-
|
411
|
+
const handlePauseResume = useCallback(() => {
|
412
|
+
if (isPaused && resumeManualSync) {
|
413
|
+
resumeManualSync()
|
414
|
+
} else if (!isPaused && pauseManualSync) {
|
415
|
+
pauseManualSync()
|
416
|
+
}
|
417
|
+
}, [isPaused, resumeManualSync, pauseManualSync])
|
418
|
+
|
419
|
+
// Memoize current word info to prevent recalculation
|
420
|
+
const currentWordInfo = useMemo(() => {
|
421
|
+
if (!isManualSyncing || syncWordIndex < 0 || syncWordIndex >= words.length) {
|
422
|
+
return null
|
423
|
+
}
|
424
|
+
|
425
|
+
return {
|
426
|
+
index: syncWordIndex + 1,
|
427
|
+
total: words.length,
|
428
|
+
text: words[syncWordIndex]?.text || ''
|
429
|
+
}
|
430
|
+
}, [isManualSyncing, syncWordIndex, words])
|
260
431
|
|
261
432
|
return (
|
262
433
|
<>
|
@@ -267,11 +438,12 @@ export default function EditTimelineSection({
|
|
267
438
|
>
|
268
439
|
<TimelineEditor
|
269
440
|
words={words}
|
270
|
-
startTime={
|
271
|
-
endTime={
|
441
|
+
startTime={effectiveTimeRange.start}
|
442
|
+
endTime={effectiveTimeRange.end}
|
272
443
|
onWordUpdate={onWordUpdate}
|
273
444
|
currentTime={currentTime}
|
274
445
|
onPlaySegment={onPlaySegment}
|
446
|
+
onUnsyncWord={onUnsyncWord}
|
275
447
|
/>
|
276
448
|
</Box>
|
277
449
|
|
@@ -282,82 +454,33 @@ export default function EditTimelineSection({
|
|
282
454
|
Current Time Range: {currentStartTime?.toFixed(2) ?? 'N/A'} - {currentEndTime?.toFixed(2) ?? 'N/A'}
|
283
455
|
</Typography>
|
284
456
|
|
285
|
-
<
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
disabled={zoomLevel <= 2}
|
310
|
-
size="small"
|
311
|
-
>
|
312
|
-
<ZoomInIcon />
|
313
|
-
</IconButton>
|
314
|
-
</Tooltip>
|
315
|
-
<Tooltip title="Scroll Right">
|
316
|
-
<IconButton
|
317
|
-
onClick={handleScrollRight}
|
318
|
-
disabled={visibleEndTime >= endTime}
|
319
|
-
size="small"
|
320
|
-
>
|
321
|
-
<ArrowForwardIcon />
|
322
|
-
</IconButton>
|
323
|
-
</Tooltip>
|
324
|
-
<Tooltip
|
325
|
-
title={autoScrollEnabled ?
|
326
|
-
"Disable Auto-Page Turn During Playback (Alt+A)" :
|
327
|
-
"Enable Auto-Page Turn During Playback (Alt+A)"}
|
328
|
-
>
|
329
|
-
<IconButton
|
330
|
-
onClick={toggleAutoScroll}
|
331
|
-
color={autoScrollEnabled ? "primary" : "default"}
|
332
|
-
size="small"
|
333
|
-
>
|
334
|
-
{autoScrollEnabled ? <AutorenewIcon /> : <PauseCircleOutlineIcon />}
|
335
|
-
</IconButton>
|
336
|
-
</Tooltip>
|
337
|
-
<Tooltip title="Jump to Current Playback Position (Alt+J)">
|
338
|
-
<IconButton
|
339
|
-
onClick={jumpToCurrentTime}
|
340
|
-
disabled={!currentTime}
|
341
|
-
size="small"
|
342
|
-
>
|
343
|
-
<CenterFocusStrongIcon />
|
344
|
-
</IconButton>
|
345
|
-
</Tooltip>
|
346
|
-
</>
|
347
|
-
)}
|
348
|
-
<Button
|
349
|
-
variant={isManualSyncing ? "outlined" : "contained"}
|
350
|
-
onClick={startManualSync}
|
351
|
-
disabled={!onPlaySegment}
|
352
|
-
startIcon={isManualSyncing ? <CancelIcon /> : <PlayCircleOutlineIcon />}
|
353
|
-
color={isManualSyncing ? "error" : "primary"}
|
354
|
-
>
|
355
|
-
{isManualSyncing ? "Cancel Sync" : "Manual Sync"}
|
356
|
-
</Button>
|
357
|
-
{isManualSyncing && (
|
457
|
+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
458
|
+
<TimelineControls
|
459
|
+
isGlobal={isGlobal}
|
460
|
+
visibleStartTime={visibleStartTime}
|
461
|
+
visibleEndTime={visibleEndTime}
|
462
|
+
startTime={startTime}
|
463
|
+
endTime={endTime}
|
464
|
+
zoomLevel={zoomLevel}
|
465
|
+
autoScrollEnabled={autoScrollEnabled}
|
466
|
+
currentTime={currentTime}
|
467
|
+
isManualSyncing={isManualSyncing}
|
468
|
+
isReplaceAllMode={isReplaceAllMode}
|
469
|
+
isPaused={isPaused}
|
470
|
+
onScrollLeft={handleScrollLeft}
|
471
|
+
onZoomOut={handleZoomOut}
|
472
|
+
onZoomIn={handleZoomIn}
|
473
|
+
onScrollRight={handleScrollRight}
|
474
|
+
onToggleAutoScroll={toggleAutoScroll}
|
475
|
+
onJumpToCurrentTime={jumpToCurrentTime}
|
476
|
+
onStartManualSync={startManualSync}
|
477
|
+
onPauseResume={handlePauseResume}
|
478
|
+
onStopAudio={onStopAudio}
|
479
|
+
/>
|
480
|
+
{currentWordInfo && (
|
358
481
|
<Box>
|
359
482
|
<Typography variant="body2">
|
360
|
-
Word {
|
483
|
+
Word {currentWordInfo.index} of {currentWordInfo.total}: <strong>{currentWordInfo.text}</strong>
|
361
484
|
</Typography>
|
362
485
|
<Typography variant="caption" color="text.secondary">
|
363
486
|
{isSpacebarPressed ?
|
@@ -366,7 +489,7 @@ export default function EditTimelineSection({
|
|
366
489
|
</Typography>
|
367
490
|
</Box>
|
368
491
|
)}
|
369
|
-
</
|
492
|
+
</Box>
|
370
493
|
</Box>
|
371
494
|
</>
|
372
495
|
)
|