Repository navigation
Expand file tree
/
Copy pathai-transcript.ts
More file actions
93 lines (84 loc) · 2.7 KB
/
Copy pathai-transcript.ts
File metadata and controls
93 lines (84 loc) · 2.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import type { AiConversationTurn } from "./ai-realtime-protocol";
import { wrapHudText } from "./fast-detail-text";
const MAXIMUM_LINE_UNITS = 58;
export const AI_TRANSCRIPT_VISIBLE_LINES = 9;
function roleLines(label: "YOU" | "AI", text: string): readonly string[] {
const normalized = text.replace(/\s+/g, " ").trim();
if (!normalized) return [];
const prefix = `${label} // `;
const continuation = " ".repeat(prefix.length);
return wrapHudText(
normalized,
MAXIMUM_LINE_UNITS - prefix.length,
1024,
).map((line, index) => `${index === 0 ? prefix : continuation}${line}`);
}
export function createAiTranscriptLines(
completed: readonly AiConversationTurn[],
current: AiConversationTurn,
): readonly string[] {
const turns = current.user.trim() || current.assistant.trim()
? [...completed, current]
: completed;
return turns.flatMap((turn) => [
...roleLines("YOU", turn.user),
...roleLines("AI", turn.assistant),
]);
}
type TranscriptSpeaker = "YOU" | "AI" | undefined;
function transcriptSpeakers(
lines: readonly string[],
): readonly TranscriptSpeaker[] {
let current: TranscriptSpeaker;
return lines.map((line) => {
if (line.startsWith("YOU // ")) current = "YOU";
else if (line.startsWith("AI // ")) current = "AI";
return current;
});
}
function displayRows(
lines: readonly string[],
speakers: readonly TranscriptSpeaker[],
startLine: number,
endLine: number,
): readonly string[] {
const rows: string[] = [];
for (let index = startLine; index <= endLine; index += 1) {
if (
index > startLine
&& speakers[index - 1] !== undefined
&& speakers[index] !== undefined
&& speakers[index - 1] !== speakers[index]
) {
rows.push("");
}
rows.push(lines[index]);
}
return rows;
}
export function selectAiTranscriptDisplayRows(
lines: readonly string[],
selectedLine: number,
visibleRows = AI_TRANSCRIPT_VISIBLE_LINES,
): readonly string[] {
const maximumRows = Math.max(0, Math.floor(visibleRows));
if (lines.length === 0 || maximumRows <= 0) return [];
const endLine = Math.min(
Math.max(0, Math.floor(selectedLine)),
lines.length - 1,
);
const speakers = transcriptSpeakers(lines);
let startLine = endLine;
let rowCount = 1;
while (startLine > 0) {
const previousLine = startLine - 1;
const includesSpeakerGap = speakers[previousLine] !== undefined
&& speakers[startLine] !== undefined
&& speakers[previousLine] !== speakers[startLine];
const additionalRows = includesSpeakerGap ? 2 : 1;
if (rowCount + additionalRows > maximumRows) break;
rowCount += additionalRows;
startLine = previousLine;
}
return displayRows(lines, speakers, startLine, endLine);
}