Show the live transcript as one flowing row of confidence chips.
Drop per-segment cards and plain text so only colored word chips wrap in a single container while transcription runs.
This commit is contained in:
@@ -171,6 +171,32 @@ export function transcriptionMonitor({ statusUrl, initial, userId }) {
|
|||||||
return this.status.has_transcript ? 'Re-transcribe' : 'Start transcription';
|
return this.status.has_transcript ? 'Re-transcribe' : 'Start transcription';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
get whisperWords() {
|
||||||
|
return this.whisper.segments.flatMap((segment, segmentIndex) => {
|
||||||
|
if (Array.isArray(segment.words) && segment.words.length) {
|
||||||
|
return segment.words.map((word, wordIndex) => ({
|
||||||
|
word: word.word,
|
||||||
|
start: word.start,
|
||||||
|
probability: word.probability,
|
||||||
|
key: [segmentIndex, wordIndex, word.start ?? '', word.word ?? ''].join(':'),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const text = typeof segment.text === 'string' ? segment.text.trim() : '';
|
||||||
|
|
||||||
|
if (text === '') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [{
|
||||||
|
word: text,
|
||||||
|
start: segment.start,
|
||||||
|
probability: null,
|
||||||
|
key: segmentIndex + ':text',
|
||||||
|
}];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
start() {
|
start() {
|
||||||
this.leaveChannel = subscribeToRecording({
|
this.leaveChannel = subscribeToRecording({
|
||||||
recordingId: this.status.id,
|
recordingId: this.status.id,
|
||||||
@@ -421,10 +447,6 @@ export function transcriptionMonitor({ statusUrl, initial, userId }) {
|
|||||||
return segments.concat([segment]);
|
return segments.concat([segment]);
|
||||||
},
|
},
|
||||||
|
|
||||||
segmentDomKey(segment, index) {
|
|
||||||
return segmentKey(segment) + '#' + index;
|
|
||||||
},
|
|
||||||
|
|
||||||
seekTo(seconds) {
|
seekTo(seconds) {
|
||||||
const player = this.$refs.player;
|
const player = this.$refs.player;
|
||||||
|
|
||||||
|
|||||||
@@ -181,7 +181,7 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
class="mt-4 whitespace-pre-wrap text-sm leading-relaxed text-zinc-800 dark:text-zinc-100"
|
class="mt-4 whitespace-pre-wrap text-sm leading-relaxed text-zinc-800 dark:text-zinc-100"
|
||||||
x-show="status.transcript && !whisper.segments.length"
|
x-show="status.transcript && !whisperWords.length"
|
||||||
x-text="status.transcript"
|
x-text="status.transcript"
|
||||||
></div>
|
></div>
|
||||||
|
|
||||||
@@ -229,7 +229,7 @@
|
|||||||
<div x-show="!status.is_active && status.has_transcript" x-cloak>
|
<div x-show="!status.is_active && status.has_transcript" x-cloak>
|
||||||
<p
|
<p
|
||||||
class="mt-4 whitespace-pre-wrap text-sm leading-relaxed text-zinc-800 dark:text-zinc-100"
|
class="mt-4 whitespace-pre-wrap text-sm leading-relaxed text-zinc-800 dark:text-zinc-100"
|
||||||
x-show="!whisper.segments.length"
|
x-show="!whisperWords.length"
|
||||||
x-text="status.transcript"
|
x-text="status.transcript"
|
||||||
></p>
|
></p>
|
||||||
<flux:text
|
<flux:text
|
||||||
@@ -243,8 +243,8 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="mt-4 space-y-4"
|
class="mt-4 space-y-3"
|
||||||
x-show="whisper.language || whisper.segments.length || whisper.logprobs.length"
|
x-show="whisper.language || whisperWords.length"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex flex-wrap gap-2 text-xs text-zinc-600 dark:text-zinc-400"
|
class="flex flex-wrap gap-2 text-xs text-zinc-600 dark:text-zinc-400"
|
||||||
@@ -260,24 +260,8 @@
|
|||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template x-for="(segment, index) in whisper.segments" :key="segmentDomKey(segment, index)">
|
<div class="flex flex-wrap gap-1" x-show="whisperWords.length">
|
||||||
<div class="rounded-lg border border-zinc-200 p-3 dark:border-zinc-700">
|
<template x-for="word in whisperWords" :key="word.key">
|
||||||
<div class="flex flex-wrap items-baseline justify-between gap-2 text-xs text-zinc-500 dark:text-zinc-400">
|
|
||||||
<span class="tabular-nums">
|
|
||||||
<span x-text="formatClock(segment.start)"></span>
|
|
||||||
–
|
|
||||||
<span x-text="formatClock(segment.end)"></span>
|
|
||||||
</span>
|
|
||||||
<span x-show="segment.avg_logprob != null">
|
|
||||||
avg logprob
|
|
||||||
<span class="font-medium text-zinc-700 dark:text-zinc-200" x-text="Number(segment.avg_logprob).toFixed(3)"></span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p class="mt-2 text-sm leading-relaxed text-zinc-800 dark:text-zinc-100" x-text="segment.text"></p>
|
|
||||||
|
|
||||||
<div class="mt-2 flex flex-wrap gap-1" x-show="segment.words && segment.words.length">
|
|
||||||
<template x-for="(word, wordIndex) in (segment.words || [])" :key="wordIndex">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="rounded px-1.5 py-0.5 text-xs tabular-nums"
|
class="rounded px-1.5 py-0.5 text-xs tabular-nums"
|
||||||
@@ -288,34 +272,6 @@
|
|||||||
></button>
|
></button>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<details class="mt-2 text-xs text-zinc-500 dark:text-zinc-400" x-show="segment.tokens && segment.tokens.length">
|
|
||||||
<summary class="cursor-pointer select-none">Tokens</summary>
|
|
||||||
<p class="mt-1 break-all font-mono" x-text="(segment.tokens || []).join(' ')"></p>
|
|
||||||
</details>
|
|
||||||
|
|
||||||
<p class="mt-1 text-xs text-zinc-500 dark:text-zinc-400" x-show="segment.no_speech_prob != null">
|
|
||||||
no-speech
|
|
||||||
<span class="font-medium" x-text="Number(segment.no_speech_prob).toFixed(3)"></span>
|
|
||||||
<span x-show="segment.compression_ratio != null">
|
|
||||||
· compression
|
|
||||||
<span class="font-medium" x-text="Number(segment.compression_ratio).toFixed(2)"></span>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<details class="text-xs text-zinc-500 dark:text-zinc-400" x-show="whisper.logprobs.length">
|
|
||||||
<summary class="cursor-pointer select-none">Token logprobs</summary>
|
|
||||||
<ul class="mt-2 space-y-1 font-mono">
|
|
||||||
<template x-for="(row, index) in whisper.logprobs" :key="index">
|
|
||||||
<li>
|
|
||||||
<span x-text="row.token || '—'"></span>
|
|
||||||
<span x-show="row.logprob != null" x-text="' ' + Number(row.logprob).toFixed(3)"></span>
|
|
||||||
</li>
|
|
||||||
</template>
|
|
||||||
</ul>
|
|
||||||
</details>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<flux:text
|
<flux:text
|
||||||
|
|||||||
@@ -166,9 +166,8 @@ class ShowTest extends TestCase
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
Livewire::test(Show::class, ['recording' => $recording])
|
Livewire::test(Show::class, ['recording' => $recording])
|
||||||
->assertSee('whisper.segments', false)
|
->assertSee('whisperWords', false)
|
||||||
->assertSee('whisper.language', false)
|
->assertSee('whisper.language', false)
|
||||||
->assertSee('segmentDomKey', false)
|
|
||||||
->assertSee('Hello');
|
->assertSee('Hello');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user