All checks were successful
Release / Bump version and tag (push) Successful in 7s
Svelte 5 runes ($state, $derived, $effect) are only compiled in .svelte and .svelte.ts files. The stores used runes in plain .ts files, which meant $state was treated as an undefined function at runtime, crashing the JS before anything rendered. - Renamed backend.ts -> backend.svelte.ts - Renamed config.ts -> config.svelte.ts - Renamed transcriptions.ts -> transcriptions.svelte.ts - Added .svelte.ts to Vite resolve extensions - Added missing obsUrl/syncUrl getters to backend store Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
110 lines
2.5 KiB
TypeScript
110 lines
2.5 KiB
TypeScript
/**
|
|
* Transcriptions store - manages the list of transcription items
|
|
* received from the backend via WebSocket.
|
|
*/
|
|
|
|
export interface TranscriptionItem {
|
|
id: string;
|
|
text: string;
|
|
userName: string;
|
|
timestamp: string;
|
|
isPreview: boolean;
|
|
}
|
|
|
|
let items = $state<TranscriptionItem[]>([]);
|
|
let nextId = 0;
|
|
|
|
function generateId(): string {
|
|
return `t-${Date.now()}-${nextId++}`;
|
|
}
|
|
|
|
function addTranscription(data: {
|
|
text?: string;
|
|
user_name?: string;
|
|
timestamp?: string;
|
|
}) {
|
|
// When a final transcription arrives, remove any existing preview
|
|
const previewIndex = items.findIndex((item) => item.isPreview);
|
|
if (previewIndex !== -1) {
|
|
items.splice(previewIndex, 1);
|
|
}
|
|
|
|
items.push({
|
|
id: generateId(),
|
|
text: data.text ?? "",
|
|
userName: data.user_name ?? "",
|
|
timestamp: data.timestamp ?? "",
|
|
isPreview: false,
|
|
});
|
|
|
|
// Keep a reasonable limit
|
|
if (items.length > 500) {
|
|
items.splice(0, items.length - 500);
|
|
}
|
|
}
|
|
|
|
function setPreview(data: {
|
|
text?: string;
|
|
user_name?: string;
|
|
timestamp?: string;
|
|
}) {
|
|
const existingIndex = items.findIndex((item) => item.isPreview);
|
|
const previewItem: TranscriptionItem = {
|
|
id: existingIndex !== -1 ? items[existingIndex].id : generateId(),
|
|
text: data.text ?? "",
|
|
userName: data.user_name ?? "",
|
|
timestamp: data.timestamp ?? "",
|
|
isPreview: true,
|
|
};
|
|
|
|
if (existingIndex !== -1) {
|
|
items[existingIndex] = previewItem;
|
|
} else {
|
|
items.push(previewItem);
|
|
}
|
|
}
|
|
|
|
function clearAll() {
|
|
items.length = 0;
|
|
}
|
|
|
|
function getPlainText(): string {
|
|
return items
|
|
.filter((item) => !item.isPreview)
|
|
.map((item) => {
|
|
let line = "";
|
|
if (item.timestamp) line += `[${item.timestamp}] `;
|
|
if (item.userName) line += `${item.userName}: `;
|
|
line += item.text;
|
|
return line;
|
|
})
|
|
.join("\n");
|
|
}
|
|
|
|
// Listen for backend events
|
|
if (typeof window !== "undefined") {
|
|
window.addEventListener("backend:transcription", ((e: CustomEvent) => {
|
|
addTranscription(e.detail);
|
|
}) as EventListener);
|
|
|
|
window.addEventListener("backend:preview", ((e: CustomEvent) => {
|
|
setPreview(e.detail);
|
|
}) as EventListener);
|
|
}
|
|
|
|
export const transcriptionStore = {
|
|
get items() {
|
|
return items;
|
|
},
|
|
get currentPreview(): TranscriptionItem | null {
|
|
return items.find((item) => item.isPreview) ?? null;
|
|
},
|
|
get transcriptions(): TranscriptionItem[] {
|
|
return items.filter((item) => !item.isPreview);
|
|
},
|
|
addTranscription,
|
|
setPreview,
|
|
clearAll,
|
|
getPlainText,
|
|
};
|