import TranscriptAssist from './TranscriptAssist'; import type {Command,Campaign,Transcript,TranscriptReview as Review,TranscriptSegment} from '../lib/types'; import {mapTranscriptSpeaker,reviewedTranscriptExcerpt,reviewLabels,reviewState} from '../lib/transcript-review'; import {download} from '../lib/api'; import {Button,Field,Modal} from './ui'; export default function TranscriptReview({transcript:t,campaign,onChange,onClose,onSave,notice,send,version,demo=false}:{transcript:Transcript;campaign:Campaign;onChange:(t:Transcript)=>void;onClose:()=>void;onSave:()=>Promise;notice:(message:string)=>void;send:(command:Command)=>Promise;version:number;demo?:boolean}){ const speakers=[...new Set(t.segments.map(s=>s.speaker))]; const reviewed=t.segments.filter(s=>reviewState(s)==='in-world').length; const patch=(index:number,values:Partial)=>onChange({...t,segments:t.segments.map((s,i)=>i===index?{...s,...values}:s)}); const characterOptions=<>{campaign.characters.map(c=>)}; return

{reviewed} In-world · {t.segments.filter(s=>reviewState(s)==='ooc').length} Table chatter · {t.segments.filter(s=>reviewState(s)==='unreviewed').length} Unreviewed

Manual review & transcript edits onChange({...t,name:e.target.value})}/>

Manual cleanup is private to the DM. Save your changes before requesting AI suggestions. Match speakers or mark dialogue only when you want a reviewed story excerpt. Leave uncertain speech Unreviewed; mark jokes and real-life talk Table chatter. Nothing here changes canon or gameplay.

Match speakers to characters · {speakers.length}

Each choice applies to this speaker’s segments. Original speaker labels stay intact; individual segments can differ.

{speakers.map(speaker=>{ const segments=t.segments.filter(s=>s.speaker===speaker),ids=new Set(segments.map(s=>s.characterId||'')); return ; })}
{t.segments.map((segment,index)=>
{index+1}. {segment.speaker||'Unassigned'} · {reviewLabels[reviewState(segment)]}{segment.time||'Untimed'}{segment.characterId?' · '+(campaign.characters.find(c=>c.id===segment.characterId)?.name||'Missing character'):''}{segment.text.slice(0,130)}{segment.text.length>130?'…':''} patch(index,{speaker:e.target.value})}/>