karaoke-gen 0.76.20__py3-none-any.whl → 0.81.1__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.
Files changed (26) hide show
  1. karaoke_gen/instrumental_review/static/index.html +179 -16
  2. karaoke_gen/karaoke_gen.py +5 -4
  3. karaoke_gen/lyrics_processor.py +25 -6
  4. {karaoke_gen-0.76.20.dist-info → karaoke_gen-0.81.1.dist-info}/METADATA +79 -3
  5. {karaoke_gen-0.76.20.dist-info → karaoke_gen-0.81.1.dist-info}/RECORD +26 -23
  6. lyrics_transcriber/core/config.py +8 -0
  7. lyrics_transcriber/core/controller.py +43 -1
  8. lyrics_transcriber/correction/agentic/providers/config.py +6 -0
  9. lyrics_transcriber/correction/agentic/providers/model_factory.py +24 -1
  10. lyrics_transcriber/correction/agentic/router.py +17 -13
  11. lyrics_transcriber/frontend/.gitignore +1 -0
  12. lyrics_transcriber/frontend/e2e/agentic-corrections.spec.ts +207 -0
  13. lyrics_transcriber/frontend/e2e/fixtures/agentic-correction-data.json +226 -0
  14. lyrics_transcriber/frontend/package.json +4 -1
  15. lyrics_transcriber/frontend/playwright.config.ts +1 -1
  16. lyrics_transcriber/frontend/src/components/CorrectedWordWithActions.tsx +34 -30
  17. lyrics_transcriber/frontend/src/components/Header.tsx +141 -34
  18. lyrics_transcriber/frontend/src/components/LyricsAnalyzer.tsx +120 -3
  19. lyrics_transcriber/frontend/src/components/TranscriptionView.tsx +11 -1
  20. lyrics_transcriber/frontend/src/components/shared/components/HighlightedText.tsx +122 -35
  21. lyrics_transcriber/frontend/src/components/shared/types.ts +6 -0
  22. lyrics_transcriber/output/generator.py +50 -3
  23. lyrics_transcriber/transcribers/local_whisper.py +260 -0
  24. {karaoke_gen-0.76.20.dist-info → karaoke_gen-0.81.1.dist-info}/WHEEL +0 -0
  25. {karaoke_gen-0.76.20.dist-info → karaoke_gen-0.81.1.dist-info}/entry_points.txt +0 -0
  26. {karaoke_gen-0.76.20.dist-info → karaoke_gen-0.81.1.dist-info}/licenses/LICENSE +0 -0
@@ -23,6 +23,7 @@ interface CorrectedWordWithActionsProps {
23
23
  onClick?: () => void
24
24
  backgroundColor?: string
25
25
  shouldFlash?: boolean
26
+ showActions?: boolean // Controls whether inline action buttons are visible
26
27
  }
27
28
 
28
29
  const WordContainer = styled(Box, {
@@ -98,7 +99,8 @@ export default function CorrectedWordWithActions({
98
99
  onAccept,
99
100
  onClick,
100
101
  backgroundColor,
101
- shouldFlash
102
+ shouldFlash,
103
+ showActions = true
102
104
  }: CorrectedWordWithActionsProps) {
103
105
  const theme = useTheme()
104
106
  const isMobile = useMediaQuery(theme.breakpoints.down('sm'))
@@ -115,7 +117,7 @@ export default function CorrectedWordWithActions({
115
117
  onClick={onClick}
116
118
  >
117
119
  <OriginalWordLabel>{originalWord}</OriginalWordLabel>
118
-
120
+
119
121
  <Box
120
122
  component="span"
121
123
  sx={{
@@ -127,40 +129,42 @@ export default function CorrectedWordWithActions({
127
129
  {word}
128
130
  </Box>
129
131
 
130
- <ActionsContainer>
131
- <Tooltip title="Revert to original" placement="top" arrow>
132
- <ActionButton
133
- size="small"
134
- onClick={(e) => handleAction(e, onRevert)}
135
- aria-label="revert correction"
136
- >
137
- <UndoIcon />
138
- </ActionButton>
139
- </Tooltip>
140
-
141
- <Tooltip title="Edit correction" placement="top" arrow>
142
- <ActionButton
143
- size="small"
144
- onClick={(e) => handleAction(e, onEdit)}
145
- aria-label="edit correction"
146
- >
147
- <EditIcon />
148
- </ActionButton>
149
- </Tooltip>
132
+ {showActions && (
133
+ <ActionsContainer>
134
+ <Tooltip title="Revert to original" placement="top" arrow>
135
+ <ActionButton
136
+ size="small"
137
+ onClick={(e) => handleAction(e, onRevert)}
138
+ aria-label="revert correction"
139
+ >
140
+ <UndoIcon />
141
+ </ActionButton>
142
+ </Tooltip>
150
143
 
151
- {!isMobile && (
152
- <Tooltip title="Accept correction" placement="top" arrow>
144
+ <Tooltip title="Edit correction" placement="top" arrow>
153
145
  <ActionButton
154
146
  size="small"
155
- onClick={(e) => handleAction(e, onAccept)}
156
- aria-label="accept correction"
157
- sx={{ color: 'success.main' }}
147
+ onClick={(e) => handleAction(e, onEdit)}
148
+ aria-label="edit correction"
158
149
  >
159
- <CheckCircleOutlineIcon />
150
+ <EditIcon />
160
151
  </ActionButton>
161
152
  </Tooltip>
162
- )}
163
- </ActionsContainer>
153
+
154
+ {!isMobile && (
155
+ <Tooltip title="Accept correction" placement="top" arrow>
156
+ <ActionButton
157
+ size="small"
158
+ onClick={(e) => handleAction(e, onAccept)}
159
+ aria-label="accept correction"
160
+ sx={{ color: 'success.main' }}
161
+ >
162
+ <CheckCircleOutlineIcon />
163
+ </ActionButton>
164
+ </Tooltip>
165
+ )}
166
+ </ActionsContainer>
167
+ )}
164
168
  </WordContainer>
165
169
  )
166
170
  }
@@ -1,4 +1,4 @@
1
- import { Box, Button, Typography, useMediaQuery, useTheme, Switch, FormControlLabel, Tooltip, Paper, IconButton } from '@mui/material'
1
+ import { Box, Button, Typography, useMediaQuery, useTheme, Switch, FormControlLabel, Tooltip, Paper, IconButton, Chip } from '@mui/material'
2
2
  import LockIcon from '@mui/icons-material/Lock'
3
3
  import UploadFileIcon from '@mui/icons-material/UploadFile'
4
4
  import FindReplaceIcon from '@mui/icons-material/FindReplace'
@@ -8,6 +8,9 @@ import RedoIcon from '@mui/icons-material/Redo'
8
8
  import TimerIcon from '@mui/icons-material/Timer'
9
9
  import RestoreIcon from '@mui/icons-material/Restore'
10
10
  import RateReviewIcon from '@mui/icons-material/RateReview'
11
+ import VisibilityIcon from '@mui/icons-material/Visibility'
12
+ import CheckCircleIcon from '@mui/icons-material/CheckCircle'
13
+ import HighlightOffIcon from '@mui/icons-material/HighlightOff'
11
14
  import { CorrectionData, InteractionMode } from '../types'
12
15
  import CorrectionMetrics from './CorrectionMetrics'
13
16
  import AgenticCorrectionMetrics from './AgenticCorrectionMetrics'
@@ -44,6 +47,13 @@ interface HeaderProps {
44
47
  canRedo: boolean
45
48
  annotationsEnabled?: boolean
46
49
  onAnnotationsToggle?: (enabled: boolean) => void
50
+ // Review mode props
51
+ reviewMode?: boolean
52
+ onReviewModeToggle?: (enabled: boolean) => void
53
+ // Batch action props
54
+ onAcceptAllCorrections?: () => void
55
+ onAcceptHighConfidenceCorrections?: () => void
56
+ onRevertAllCorrections?: () => void
47
57
  }
48
58
 
49
59
  export default function Header({
@@ -70,6 +80,11 @@ export default function Header({
70
80
  canRedo,
71
81
  annotationsEnabled = true,
72
82
  onAnnotationsToggle,
83
+ reviewMode = false,
84
+ onReviewModeToggle,
85
+ onAcceptAllCorrections,
86
+ onAcceptHighConfidenceCorrections,
87
+ onRevertAllCorrections,
73
88
  }: HeaderProps) {
74
89
  const theme = useTheme()
75
90
  const isMobile = useMediaQuery(theme.breakpoints.down('md'))
@@ -129,19 +144,69 @@ export default function Header({
129
144
  </Box>
130
145
  )}
131
146
 
132
- {isReadOnly && (
133
- <Box sx={{ display: 'flex', justifyContent: 'flex-end', mb: 1 }}>
134
- <Button
135
- variant="outlined"
136
- size="small"
137
- startIcon={<UploadFileIcon />}
138
- onClick={onFileLoad}
139
- fullWidth={isMobile}
140
- >
141
- Load File
142
- </Button>
147
+ <Box sx={{
148
+ display: 'flex',
149
+ flexDirection: isMobile ? 'column' : 'row',
150
+ gap: 1,
151
+ justifyContent: 'space-between',
152
+ alignItems: isMobile ? 'stretch' : 'center',
153
+ mb: 1
154
+ }}>
155
+ <Typography variant="h4" sx={{ fontSize: isMobile ? '1.3rem' : '1.5rem' }}>
156
+ Nomad Karaoke: Lyrics Transcription Review
157
+ </Typography>
158
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
159
+ {!isReadOnly && isAgenticMode && onReviewModeToggle && (
160
+ <Tooltip title={reviewMode
161
+ ? "Hide inline correction actions"
162
+ : "Show inline actions on all corrections for quick review"
163
+ }>
164
+ <Chip
165
+ icon={<VisibilityIcon />}
166
+ label={reviewMode ? "Review Mode" : "Review Off"}
167
+ onClick={() => onReviewModeToggle(!reviewMode)}
168
+ color={reviewMode ? "secondary" : "default"}
169
+ variant={reviewMode ? "filled" : "outlined"}
170
+ size="small"
171
+ sx={{
172
+ cursor: 'pointer',
173
+ '& .MuiChip-icon': { fontSize: '1rem' }
174
+ }}
175
+ />
176
+ </Tooltip>
177
+ )}
178
+ {!isReadOnly && onAnnotationsToggle && (
179
+ <Tooltip title={annotationsEnabled
180
+ ? "Click to disable annotation prompts when editing"
181
+ : "Click to enable annotation prompts when editing"
182
+ }>
183
+ <Chip
184
+ icon={<RateReviewIcon />}
185
+ label={annotationsEnabled ? "Feedback On" : "Feedback Off"}
186
+ onClick={() => onAnnotationsToggle(!annotationsEnabled)}
187
+ color={annotationsEnabled ? "primary" : "default"}
188
+ variant={annotationsEnabled ? "filled" : "outlined"}
189
+ size="small"
190
+ sx={{
191
+ cursor: 'pointer',
192
+ '& .MuiChip-icon': { fontSize: '1rem' }
193
+ }}
194
+ />
195
+ </Tooltip>
196
+ )}
197
+ {isReadOnly && (
198
+ <Button
199
+ variant="outlined"
200
+ size="small"
201
+ startIcon={<UploadFileIcon />}
202
+ onClick={onFileLoad}
203
+ fullWidth={isMobile}
204
+ >
205
+ Load File
206
+ </Button>
207
+ )}
143
208
  </Box>
144
- )}
209
+ </Box>
145
210
 
146
211
  <Box sx={{
147
212
  display: 'flex',
@@ -250,6 +315,65 @@ export default function Header({
250
315
  </Box>
251
316
  </Box>
252
317
 
318
+ {/* Batch Actions Panel - shown when review mode is enabled */}
319
+ {!isReadOnly && reviewMode && isAgenticMode && data.corrections?.length > 0 && (
320
+ <Paper sx={{ p: 0.8, mb: 1, backgroundColor: 'action.hover' }}>
321
+ <Box sx={{
322
+ display: 'flex',
323
+ flexDirection: isMobile ? 'column' : 'row',
324
+ gap: 1,
325
+ alignItems: isMobile ? 'stretch' : 'center',
326
+ justifyContent: 'space-between'
327
+ }}>
328
+ <Typography variant="subtitle2" sx={{ fontSize: '0.75rem', color: 'text.secondary' }}>
329
+ Batch Actions ({data.corrections.length} corrections)
330
+ </Typography>
331
+ <Box sx={{
332
+ display: 'flex',
333
+ gap: 1,
334
+ flexWrap: 'wrap'
335
+ }}>
336
+ {onAcceptHighConfidenceCorrections && (
337
+ <Button
338
+ variant="contained"
339
+ size="small"
340
+ color="success"
341
+ startIcon={<CheckCircleIcon />}
342
+ onClick={onAcceptHighConfidenceCorrections}
343
+ sx={{ fontSize: '0.75rem', py: 0.5 }}
344
+ >
345
+ Accept High Confidence ({data.corrections.filter(c => c.confidence >= 0.8).length})
346
+ </Button>
347
+ )}
348
+ {onAcceptAllCorrections && (
349
+ <Button
350
+ variant="outlined"
351
+ size="small"
352
+ color="success"
353
+ startIcon={<CheckCircleIcon />}
354
+ onClick={onAcceptAllCorrections}
355
+ sx={{ fontSize: '0.75rem', py: 0.5 }}
356
+ >
357
+ Accept All
358
+ </Button>
359
+ )}
360
+ {onRevertAllCorrections && (
361
+ <Button
362
+ variant="outlined"
363
+ size="small"
364
+ color="error"
365
+ startIcon={<HighlightOffIcon />}
366
+ onClick={onRevertAllCorrections}
367
+ sx={{ fontSize: '0.75rem', py: 0.5 }}
368
+ >
369
+ Revert All
370
+ </Button>
371
+ )}
372
+ </Box>
373
+ </Box>
374
+ </Paper>
375
+ )}
376
+
253
377
  <Paper sx={{ p: 0.8, mb: 1 }}>
254
378
  <Box sx={{
255
379
  display: 'flex',
@@ -356,10 +480,10 @@ export default function Header({
356
480
  Timing Offset
357
481
  </Button>
358
482
  {timingOffsetMs !== 0 && (
359
- <Typography
360
- variant="body2"
361
- sx={{
362
- ml: 1,
483
+ <Typography
484
+ variant="body2"
485
+ sx={{
486
+ ml: 1,
363
487
  fontWeight: 'bold',
364
488
  color: theme.palette.secondary.main
365
489
  }}
@@ -369,23 +493,6 @@ export default function Header({
369
493
  )}
370
494
  </Box>
371
495
  )}
372
- {!isReadOnly && onAnnotationsToggle && (
373
- <Tooltip title={annotationsEnabled
374
- ? "Click to disable annotation prompts when editing"
375
- : "Click to enable annotation prompts when editing"
376
- }>
377
- <Button
378
- variant="outlined"
379
- size="small"
380
- onClick={() => onAnnotationsToggle(!annotationsEnabled)}
381
- startIcon={<RateReviewIcon />}
382
- color={annotationsEnabled ? "primary" : "inherit"}
383
- sx={{ minWidth: 'fit-content', height: '32px' }}
384
- >
385
- {annotationsEnabled ? "Feedback On" : "Feedback Off"}
386
- </Button>
387
- </Tooltip>
388
- )}
389
496
  <AudioPlayer
390
497
  apiClient={apiClient}
391
498
  onTimeUpdate={onTimeUpdate}
@@ -88,6 +88,12 @@ interface MemoizedTranscriptionViewProps {
88
88
  anchors: AnchorSequence[]
89
89
  disableHighlighting: boolean
90
90
  onDataChange?: (updatedData: CorrectionData) => void
91
+ // Review mode props
92
+ reviewMode: boolean
93
+ onRevertCorrection: (wordId: string) => void
94
+ onEditCorrection: (wordId: string) => void
95
+ onAcceptCorrection: (wordId: string) => void
96
+ onShowCorrectionDetail: (wordId: string) => void
91
97
  }
92
98
 
93
99
  // Create a memoized TranscriptionView component
@@ -103,7 +109,12 @@ const MemoizedTranscriptionView = memo(function MemoizedTranscriptionView({
103
109
  currentTime,
104
110
  anchors,
105
111
  disableHighlighting,
106
- onDataChange
112
+ onDataChange,
113
+ reviewMode,
114
+ onRevertCorrection,
115
+ onEditCorrection,
116
+ onAcceptCorrection,
117
+ onShowCorrectionDetail
107
118
  }: MemoizedTranscriptionViewProps) {
108
119
  return (
109
120
  <TranscriptionView
@@ -118,6 +129,11 @@ const MemoizedTranscriptionView = memo(function MemoizedTranscriptionView({
118
129
  currentTime={disableHighlighting ? undefined : currentTime}
119
130
  anchors={anchors}
120
131
  onDataChange={onDataChange}
132
+ reviewMode={reviewMode}
133
+ onRevertCorrection={onRevertCorrection}
134
+ onEditCorrection={onEditCorrection}
135
+ onAcceptCorrection={onAcceptCorrection}
136
+ onShowCorrectionDetail={onShowCorrectionDetail}
121
137
  />
122
138
  );
123
139
  });
@@ -202,6 +218,13 @@ interface MemoizedHeaderProps {
202
218
  onUnCorrectAll: () => void
203
219
  annotationsEnabled: boolean
204
220
  onAnnotationsToggle: (enabled: boolean) => void
221
+ // Review mode props
222
+ reviewMode: boolean
223
+ onReviewModeToggle: (enabled: boolean) => void
224
+ // Batch action props
225
+ onAcceptAllCorrections: () => void
226
+ onAcceptHighConfidenceCorrections: () => void
227
+ onRevertAllCorrections: () => void
205
228
  }
206
229
 
207
230
  // Create a memoized Header component
@@ -228,7 +251,12 @@ const MemoizedHeader = memo(function MemoizedHeader({
228
251
  canRedo,
229
252
  onUnCorrectAll,
230
253
  annotationsEnabled,
231
- onAnnotationsToggle
254
+ onAnnotationsToggle,
255
+ reviewMode,
256
+ onReviewModeToggle,
257
+ onAcceptAllCorrections,
258
+ onAcceptHighConfidenceCorrections,
259
+ onRevertAllCorrections
232
260
  }: MemoizedHeaderProps) {
233
261
  return (
234
262
  <Header
@@ -255,6 +283,11 @@ const MemoizedHeader = memo(function MemoizedHeader({
255
283
  onUnCorrectAll={onUnCorrectAll}
256
284
  annotationsEnabled={annotationsEnabled}
257
285
  onAnnotationsToggle={onAnnotationsToggle}
286
+ reviewMode={reviewMode}
287
+ onReviewModeToggle={onReviewModeToggle}
288
+ onAcceptAllCorrections={onAcceptAllCorrections}
289
+ onAcceptHighConfidenceCorrections={onAcceptHighConfidenceCorrections}
290
+ onRevertAllCorrections={onRevertAllCorrections}
258
291
  />
259
292
  );
260
293
  });
@@ -291,6 +324,9 @@ export default function LyricsAnalyzer({ data: initialData, onFileLoad, apiClien
291
324
  const [isFindReplaceModalOpen, setIsFindReplaceModalOpen] = useState(false)
292
325
  const [isTimingOffsetModalOpen, setIsTimingOffsetModalOpen] = useState(false)
293
326
  const [timingOffsetMs, setTimingOffsetMs] = useState(0)
327
+
328
+ // Review mode state for agentic corrections
329
+ const [reviewMode, setReviewMode] = useState(false)
294
330
 
295
331
  // Annotation collection state
296
332
  const [annotations, setAnnotations] = useState<Omit<CorrectionAnnotation, 'annotation_id' | 'timestamp'>[]>([])
@@ -793,11 +829,82 @@ export default function LyricsAnalyzer({ data: initialData, onFileLoad, apiClien
793
829
  // For now, just log acceptance
794
830
  // In the future, this could be tracked in the annotation system
795
831
  console.log('Accepted correction for word:', wordId)
796
-
832
+
797
833
  // TODO: Track acceptance in annotation system
798
834
  // This could be used to build confidence in the AI's corrections over time
799
835
  }, [])
800
836
 
837
+ // Batch action handlers for review mode
838
+ const handleAcceptAllCorrections = useCallback(() => {
839
+ // Accept all corrections - for now just log, could track in annotation system
840
+ data.corrections?.forEach(c => {
841
+ console.log('Batch accepted correction:', c.corrected_word_id || c.word_id)
842
+ })
843
+ console.log(`Accepted all ${data.corrections?.length || 0} corrections`)
844
+ }, [data.corrections])
845
+
846
+ const handleAcceptHighConfidenceCorrections = useCallback(() => {
847
+ // Accept corrections with confidence >= 80%
848
+ const highConfidence = data.corrections?.filter(c => c.confidence >= 0.8) || []
849
+ highConfidence.forEach(c => {
850
+ console.log('Batch accepted high-confidence correction:', c.corrected_word_id || c.word_id)
851
+ })
852
+ console.log(`Accepted ${highConfidence.length} high-confidence corrections`)
853
+ }, [data.corrections])
854
+
855
+ const handleRevertAllCorrections = useCallback(() => {
856
+ if (!window.confirm(`Are you sure you want to revert all ${data.corrections?.length || 0} corrections? This cannot be undone.`)) {
857
+ return
858
+ }
859
+
860
+ // Revert all corrections by reverting each one
861
+ // Process in reverse order to avoid ID conflicts
862
+ const corrections = [...(data.corrections || [])].reverse()
863
+
864
+ let newData = data
865
+ for (const correction of corrections) {
866
+ const wordId = correction.corrected_word_id || correction.word_id
867
+
868
+ // Find the segment containing the corrected word
869
+ const segmentIndex = newData.corrected_segments.findIndex(segment =>
870
+ segment.words.some(w => w.id === wordId)
871
+ )
872
+
873
+ if (segmentIndex === -1) continue
874
+
875
+ const segment = newData.corrected_segments[segmentIndex]
876
+
877
+ // Replace the corrected word with the original
878
+ const newWords = segment.words.map(word => {
879
+ if (word.id === wordId) {
880
+ return {
881
+ ...word,
882
+ text: correction.original_word,
883
+ id: correction.word_id
884
+ }
885
+ }
886
+ return word
887
+ })
888
+
889
+ const newText = newWords.map(w => w.text).join(' ')
890
+ const newSegment = { ...segment, words: newWords, text: newText }
891
+ const newSegments = newData.corrected_segments.map((seg, idx) =>
892
+ idx === segmentIndex ? newSegment : seg
893
+ )
894
+
895
+ newData = {
896
+ ...newData,
897
+ corrected_segments: newSegments,
898
+ corrections: newData.corrections?.filter(c =>
899
+ c.corrected_word_id !== wordId && c.word_id !== wordId
900
+ ) || []
901
+ }
902
+ }
903
+
904
+ updateDataWithHistory(newData, 'revert all corrections')
905
+ console.log(`Reverted all ${corrections.length} corrections`)
906
+ }, [data, updateDataWithHistory])
907
+
801
908
  const handleShowCorrectionDetail = useCallback((wordId: string) => {
802
909
  // Find the correction for this word
803
910
  const correction = data.corrections?.find(c =>
@@ -1177,6 +1284,11 @@ export default function LyricsAnalyzer({ data: initialData, onFileLoad, apiClien
1177
1284
  onUnCorrectAll={handleUnCorrectAll}
1178
1285
  annotationsEnabled={annotationsEnabled}
1179
1286
  onAnnotationsToggle={handleAnnotationsToggle}
1287
+ reviewMode={reviewMode}
1288
+ onReviewModeToggle={setReviewMode}
1289
+ onAcceptAllCorrections={handleAcceptAllCorrections}
1290
+ onAcceptHighConfidenceCorrections={handleAcceptHighConfidenceCorrections}
1291
+ onRevertAllCorrections={handleRevertAllCorrections}
1180
1292
  />
1181
1293
 
1182
1294
  <Grid container direction={isMobile ? 'column' : 'row'}>
@@ -1198,6 +1310,11 @@ export default function LyricsAnalyzer({ data: initialData, onFileLoad, apiClien
1198
1310
  // needs to update history
1199
1311
  updateDataWithHistory(updatedData, 'direct data change');
1200
1312
  }}
1313
+ reviewMode={reviewMode}
1314
+ onRevertCorrection={handleRevertCorrection}
1315
+ onEditCorrection={handleEditCorrection}
1316
+ onAcceptCorrection={handleAcceptCorrection}
1317
+ onShowCorrectionDetail={handleShowCorrectionDetail}
1201
1318
  />
1202
1319
  {!isReadOnly && apiClient && (
1203
1320
  <Box sx={{
@@ -54,7 +54,12 @@ export default function TranscriptionView({
54
54
  onPlaySegment,
55
55
  currentTime = 0,
56
56
  anchors = [],
57
- onDataChange
57
+ onDataChange,
58
+ reviewMode = false,
59
+ onRevertCorrection,
60
+ onEditCorrection,
61
+ onAcceptCorrection,
62
+ onShowCorrectionDetail
58
63
  }: TranscriptionViewProps) {
59
64
  const [selectedSegmentIndex, setSelectedSegmentIndex] = useState<number | null>(null)
60
65
  const [viewMode, setViewMode] = useState<'text' | 'duration'>('text')
@@ -237,6 +242,11 @@ export default function TranscriptionView({
237
242
  currentTime={currentTime}
238
243
  gaps={data.gap_sequences}
239
244
  corrections={data.corrections}
245
+ reviewMode={reviewMode}
246
+ onRevertCorrection={onRevertCorrection}
247
+ onEditCorrection={onEditCorrection}
248
+ onAcceptCorrection={onAcceptCorrection}
249
+ onShowCorrectionDetail={onShowCorrectionDetail}
240
250
  />
241
251
  </TextContainer>
242
252
  </Box>