Add segment splitting and editing in script review

- Added ability to edit segment text directly in textarea
- Added split segment button to divide segments at natural break points
- Added delete segment button to remove unwanted segments
- Split logic finds sentence/quote boundaries for natural splits
- All segments maintain speaker assignments after split
This commit is contained in:
2025-12-09 13:55:16 +00:00
parent 55f630a479
commit a8636bcf71
2 changed files with 102 additions and 2 deletions

View File

@@ -103,6 +103,49 @@ function App() {
)
}
const handleSegmentTextChange = (segmentId: string, newText: string) => {
setScriptSegments(prev =>
prev.map(segment =>
segment.id === segmentId
? { ...segment, text: newText }
: segment
)
)
}
const handleSegmentSplit = (segmentId: string, splitPosition: number) => {
setScriptSegments(prev => {
const segmentIndex = prev.findIndex(s => s.id === segmentId)
if (segmentIndex === -1) return prev
const segment = prev[segmentIndex]
const firstPart = segment.text.substring(0, splitPosition).trim()
const secondPart = segment.text.substring(splitPosition).trim()
if (!firstPart || !secondPart) return prev
// Create new segments
const newSegments = [...prev]
newSegments[segmentIndex] = {
...segment,
text: firstPart
}
// Insert second part after the first
newSegments.splice(segmentIndex + 1, 0, {
id: `${segmentId}-split-${Date.now()}`,
speaker: segment.speaker, // Keep same speaker by default
text: secondPart
})
return newSegments
})
}
const handleSegmentDelete = (segmentId: string) => {
setScriptSegments(prev => prev.filter(segment => segment.id !== segmentId))
}
const handleGenerate = async () => {
setIsGenerating(true)
setError(null)
@@ -265,6 +308,9 @@ function App() {
segments={scriptSegments}
roles={roles}
onSegmentChange={handleSegmentChange}
onSegmentTextChange={handleSegmentTextChange}
onSegmentSplit={handleSegmentSplit}
onSegmentDelete={handleSegmentDelete}
onGenerate={handleGenerate}
onSkipReview={handleSkipReview}
isGenerating={isGenerating}

View File

@@ -4,6 +4,9 @@ interface ScriptReviewSectionProps {
segments: ScriptSegment[];
roles: Role[];
onSegmentChange: (segmentId: string, newSpeaker: string) => void;
onSegmentTextChange: (segmentId: string, newText: string) => void;
onSegmentSplit: (segmentId: string, splitPosition: number) => void;
onSegmentDelete: (segmentId: string) => void;
onGenerate: () => void;
onSkipReview: () => void;
isGenerating: boolean;
@@ -13,6 +16,9 @@ export default function ScriptReviewSection({
segments,
roles,
onSegmentChange,
onSegmentTextChange,
onSegmentSplit,
onSegmentDelete,
onGenerate,
onSkipReview,
isGenerating,
@@ -21,6 +27,26 @@ export default function ScriptReviewSection({
onSegmentChange(segmentId, newSpeaker);
};
const handleTextChange = (segmentId: string, newText: string) => {
onSegmentTextChange(segmentId, newText);
};
const handleSplit = (segmentId: string, text: string) => {
// Find a good split point (after a sentence or quote)
const quoteMatch = text.match(/^(.*?[.!?]"?\s*)(.*)/s);
if (quoteMatch && quoteMatch[1].length > 0 && quoteMatch[2].length > 0) {
onSegmentSplit(segmentId, quoteMatch[1].length);
return;
}
// Fall back to middle split
const midPoint = Math.floor(text.length / 2);
const splitPoint = text.lastIndexOf(' ', midPoint);
if (splitPoint > 0) {
onSegmentSplit(segmentId, splitPoint + 1);
}
};
return (
<div className="max-w-5xl mx-auto">
<div className="card-modern p-10 sm:p-12 space-y-10">
@@ -66,8 +92,36 @@ export default function ScriptReviewSection({
</div>
{/* Segment Text */}
<div className="p-5 bg-white rounded-xl border-2 border-gray-200 text-gray-800 whitespace-pre-wrap leading-relaxed text-base">
{segment.text}
<textarea
value={segment.text}
onChange={(e) => handleTextChange(segment.id, e.target.value)}
className="w-full p-5 bg-white rounded-xl border-2 border-gray-200 text-gray-800 leading-relaxed text-base focus:border-indigo-500 focus:ring-4 focus:ring-indigo-100 focus:outline-none resize-y min-h-[100px] font-sans"
disabled={isGenerating}
/>
{/* Action Buttons */}
<div className="flex gap-2 mt-4">
<button
onClick={() => handleSplit(segment.id, segment.text)}
disabled={isGenerating || segment.text.length < 20}
className="px-4 py-2 text-sm font-semibold text-indigo-600 bg-indigo-50 rounded-lg hover:bg-indigo-100 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center gap-2"
title="Split this segment into two parts"
>
<span>✂️</span>
Split Segment
</button>
{segments.length > 1 && (
<button
onClick={() => onSegmentDelete(segment.id)}
disabled={isGenerating}
className="px-4 py-2 text-sm font-semibold text-red-600 bg-red-50 rounded-lg hover:bg-red-100 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center gap-2"
title="Delete this segment"
>
<span>🗑️</span>
Delete
</button>
)}
</div>
</div>
))}