122 lines
No EOL
3.7 KiB
TypeScript
122 lines
No EOL
3.7 KiB
TypeScript
"use client";
|
|
|
|
import React, { createContext, useContext, useState, FC, PropsWithChildren } from "react";
|
|
import { v4 as uuidv4 } from "uuid";
|
|
import { TranscriptItem } from "@/app/components/Realtime/types";
|
|
|
|
type TranscriptContextValue = {
|
|
transcriptItems: TranscriptItem[];
|
|
addTranscriptMessage: (itemId: string, role: "user" | "assistant", text: string, hidden?: boolean) => void;
|
|
updateTranscriptMessage: (itemId: string, text: string, isDelta: boolean) => void;
|
|
addTranscriptBreadcrumb: (title: string, data?: Record<string, any>) => void;
|
|
toggleTranscriptItemExpand: (itemId: string) => void;
|
|
updateTranscriptItemStatus: (itemId: string, newStatus: "IN_PROGRESS" | "DONE") => void;
|
|
};
|
|
|
|
const TranscriptContext = createContext<TranscriptContextValue | undefined>(undefined);
|
|
|
|
export const TranscriptProvider: FC<PropsWithChildren> = ({ children }) => {
|
|
const [transcriptItems, setTranscriptItems] = useState<TranscriptItem[]>([]);
|
|
|
|
function newTimestampPretty(): string {
|
|
return new Date().toLocaleTimeString([], {
|
|
hour12: true,
|
|
hour: "numeric",
|
|
minute: "2-digit",
|
|
second: "2-digit",
|
|
});
|
|
}
|
|
|
|
const addTranscriptMessage: TranscriptContextValue["addTranscriptMessage"] = (itemId, role, text = "", isHidden = false) => {
|
|
setTranscriptItems((prev) => {
|
|
if (prev.some((log) => log.itemId === itemId && log.type === "MESSAGE")) {
|
|
console.warn(`[addTranscriptMessage] skipping; message already exists for itemId=${itemId}, role=${role}, text=${text}`);
|
|
return prev;
|
|
}
|
|
|
|
const newItem: TranscriptItem = {
|
|
itemId,
|
|
type: "MESSAGE",
|
|
role,
|
|
title: text,
|
|
expanded: false,
|
|
timestamp: newTimestampPretty(),
|
|
createdAtMs: Date.now(),
|
|
status: "IN_PROGRESS",
|
|
isHidden,
|
|
};
|
|
|
|
return [...prev, newItem];
|
|
});
|
|
};
|
|
|
|
const updateTranscriptMessage: TranscriptContextValue["updateTranscriptMessage"] = (itemId, newText, append = false) => {
|
|
setTranscriptItems((prev) =>
|
|
prev.map((item) => {
|
|
if (item.itemId === itemId && item.type === "MESSAGE") {
|
|
return {
|
|
...item,
|
|
title: append ? (item.title ?? "") + newText : newText,
|
|
};
|
|
}
|
|
return item;
|
|
})
|
|
);
|
|
};
|
|
|
|
const addTranscriptBreadcrumb: TranscriptContextValue["addTranscriptBreadcrumb"] = (title, data) => {
|
|
setTranscriptItems((prev) => [
|
|
...prev,
|
|
{
|
|
itemId: `breadcrumb-${uuidv4()}`,
|
|
type: "BREADCRUMB",
|
|
title,
|
|
data,
|
|
expanded: false,
|
|
timestamp: newTimestampPretty(),
|
|
createdAtMs: Date.now(),
|
|
status: "DONE",
|
|
isHidden: false,
|
|
},
|
|
]);
|
|
};
|
|
|
|
const toggleTranscriptItemExpand: TranscriptContextValue["toggleTranscriptItemExpand"] = (itemId) => {
|
|
setTranscriptItems((prev) =>
|
|
prev.map((log) =>
|
|
log.itemId === itemId ? { ...log, expanded: !log.expanded } : log
|
|
)
|
|
);
|
|
};
|
|
|
|
const updateTranscriptItemStatus: TranscriptContextValue["updateTranscriptItemStatus"] = (itemId, newStatus) => {
|
|
setTranscriptItems((prev) =>
|
|
prev.map((item) =>
|
|
item.itemId === itemId ? { ...item, status: newStatus } : item
|
|
)
|
|
);
|
|
};
|
|
|
|
return (
|
|
<TranscriptContext.Provider
|
|
value={{
|
|
transcriptItems,
|
|
addTranscriptMessage,
|
|
updateTranscriptMessage,
|
|
addTranscriptBreadcrumb,
|
|
toggleTranscriptItemExpand,
|
|
updateTranscriptItemStatus,
|
|
}}
|
|
>
|
|
{children}
|
|
</TranscriptContext.Provider>
|
|
);
|
|
};
|
|
|
|
export function useTranscript() {
|
|
const context = useContext(TranscriptContext);
|
|
if (!context) {
|
|
throw new Error("useTranscript must be used within a TranscriptProvider");
|
|
}
|
|
return context;
|
|
} |