fix: transcription segment dedup, store finalisation, and panel colours
- transcriptionService: track finalizedSegmentCount so only newly-final segments are emitted per WS message (was re-processing full array each time, causing the live segment to freeze in the completed list) - transcriptionStore: saveSegment isFinal branch now appends the passed text directly instead of currentSegment (currentSegment was stale relative to the incoming final) - CCTranscriptionPanel: record button colour changed from var(--color-text) to explicit #2563eb so it is visible in dark mode; completed segment backgrounds changed from hardcoded #fff to var(--color-muted) so text is readable in both themes; keyword Add button gets same blue fix Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
0345258247
commit
308889937c
@ -249,11 +249,9 @@ export const useTranscriptionStore = create<TranscriptionState>((set, get) => ({
|
||||
const { completedSegments, currentSegment, activeSession, wordCount } = get();
|
||||
|
||||
if (isFinal) {
|
||||
// Final segment - move current to completed, clear current
|
||||
const newCompleted = [...completedSegments];
|
||||
if (currentSegment && currentSegment.text.trim()) {
|
||||
newCompleted.push({ ...currentSegment, isFinal: true });
|
||||
}
|
||||
// Final segment — append the finalized text directly (not currentSegment, which
|
||||
// may lag behind or duplicate when WhisperLive re-sends the full segments array).
|
||||
const newCompleted = [...completedSegments, { text, isFinal: true, ...metadata }];
|
||||
const newWordCount = newCompleted.reduce(
|
||||
(sum, seg) => sum + seg.text.trim().split(/\s+/).filter(Boolean).length,
|
||||
0
|
||||
|
||||
@ -14,6 +14,7 @@ export class TranscriptionService {
|
||||
private mediaStreamSource: MediaStreamAudioSourceNode | null = null;
|
||||
private workletNode: AudioWorkletNode | null = null;
|
||||
private selectedDeviceId: string = '';
|
||||
private finalizedSegmentCount: number = 0;
|
||||
private onTranscriptionUpdate: ((text: string, isFinal: boolean, metadata: { start: number, end: number }) => void) | null = null;
|
||||
|
||||
constructor(deviceId: string = '') {
|
||||
@ -99,39 +100,27 @@ export class TranscriptionService {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.onTranscriptionUpdate && data.segments) {
|
||||
// Get the last segment which is the current one being updated
|
||||
const lastSegment = data.segments[data.segments.length - 1];
|
||||
|
||||
// Process completed segments
|
||||
let lastCompletedText = '';
|
||||
for (let i = 0; i < data.segments.length - 1; i++) {
|
||||
const segment = data.segments[i];
|
||||
// Only send update if this segment is different from the last one
|
||||
if (segment.text.trim() !== lastCompletedText.trim()) {
|
||||
this.onTranscriptionUpdate(
|
||||
segment.text,
|
||||
segment.completed ?? true, // Server marks completed segments
|
||||
{
|
||||
start: parseFloat(segment.start),
|
||||
end: parseFloat(segment.end)
|
||||
}
|
||||
);
|
||||
lastCompletedText = segment.text;
|
||||
}
|
||||
if (this.onTranscriptionUpdate && data.segments && data.segments.length > 0) {
|
||||
const segments = data.segments;
|
||||
const lastIdx = segments.length - 1;
|
||||
|
||||
// Only emit segments we have not finalized yet — avoids re-processing the
|
||||
// full array on every message (which caused the "stuck last segment" bug).
|
||||
for (let i = this.finalizedSegmentCount; i < lastIdx; i++) {
|
||||
const seg = segments[i];
|
||||
this.onTranscriptionUpdate(seg.text, true, {
|
||||
start: parseFloat(seg.start),
|
||||
end: parseFloat(seg.end),
|
||||
});
|
||||
this.finalizedSegmentCount = i + 1;
|
||||
}
|
||||
|
||||
// Update the current (incomplete) segment only if it's different from the last completed one
|
||||
if (lastSegment && lastSegment.text.trim() !== lastCompletedText.trim()) {
|
||||
this.onTranscriptionUpdate(
|
||||
lastSegment.text,
|
||||
lastSegment.completed ?? false, // Last segment is typically incomplete unless marked otherwise
|
||||
{
|
||||
start: parseFloat(lastSegment.start),
|
||||
end: parseFloat(lastSegment.end)
|
||||
}
|
||||
);
|
||||
}
|
||||
// Always update the live (last) segment
|
||||
const lastSeg = segments[lastIdx];
|
||||
this.onTranscriptionUpdate(lastSeg.text, lastSeg.completed ?? false, {
|
||||
start: parseFloat(lastSeg.start),
|
||||
end: parseFloat(lastSeg.end),
|
||||
});
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
@ -190,6 +179,7 @@ export class TranscriptionService {
|
||||
}
|
||||
|
||||
private cleanup() {
|
||||
this.finalizedSegmentCount = 0;
|
||||
if (this.workletNode) {
|
||||
this.workletNode.disconnect();
|
||||
this.workletNode = null;
|
||||
|
||||
@ -365,7 +365,7 @@ export const CCTranscriptionPanel: React.FC = () => {
|
||||
fontSize: "14px",
|
||||
fontWeight: 600,
|
||||
color: "#fff",
|
||||
backgroundColor: isRecording ? "#ef4444" : "var(--color-text)",
|
||||
backgroundColor: isRecording ? "#ef4444" : "#2563eb",
|
||||
transition: "background-color 200ms ease",
|
||||
}}
|
||||
>
|
||||
@ -538,7 +538,7 @@ export const CCTranscriptionPanel: React.FC = () => {
|
||||
key={"completed-" + i}
|
||||
style={{
|
||||
padding: "8px 10px",
|
||||
backgroundColor: "#fff",
|
||||
backgroundColor: "var(--color-muted)",
|
||||
borderRadius: "4px",
|
||||
border: "1px solid var(--color-divider)",
|
||||
fontSize: "13px",
|
||||
@ -614,7 +614,7 @@ export const CCTranscriptionPanel: React.FC = () => {
|
||||
key={session.id}
|
||||
style={{
|
||||
padding: "10px",
|
||||
backgroundColor: "#fff",
|
||||
backgroundColor: "var(--color-muted)",
|
||||
borderRadius: "4px",
|
||||
border: "1px solid var(--color-divider)",
|
||||
}}
|
||||
@ -694,7 +694,7 @@ export const CCTranscriptionPanel: React.FC = () => {
|
||||
padding: "6px 10px",
|
||||
border: "none",
|
||||
borderRadius: "4px",
|
||||
backgroundColor: newKeyword.trim() ? "var(--color-text)" : "var(--color-divider)",
|
||||
backgroundColor: newKeyword.trim() ? "#2563eb" : "var(--color-divider)",
|
||||
color: "#fff",
|
||||
cursor: newKeyword.trim() ? "pointer" : "not-allowed",
|
||||
fontSize: "13px",
|
||||
@ -722,7 +722,7 @@ export const CCTranscriptionPanel: React.FC = () => {
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
padding: "6px 10px",
|
||||
backgroundColor: "#fff",
|
||||
backgroundColor: "var(--color-muted)",
|
||||
borderRadius: "4px",
|
||||
border: "1px solid var(--color-divider)",
|
||||
}}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user