101 | {[...transcriptItems]
102 | .sort((a, b) => a.createdAtMs - b.createdAtMs)
103 | .map((item) => {
104 | const {
105 | itemId,
106 | type,
107 | role,
108 | data,
109 | expanded,
110 | timestamp,
111 | title = "",
112 | isHidden,
113 | guardrailResult,
114 | } = item;
115 |
116 | if (isHidden) {
117 | return null;
118 | }
119 |
120 | if (type === "MESSAGE") {
121 | const isUser = role === "user";
122 | const containerClasses = `flex justify-end flex-col ${
123 | isUser ? "items-end" : "items-start"
124 | }`;
125 | const bubbleBase = `max-w-lg p-3 ${
126 | isUser ? "bg-gray-900 text-gray-100" : "bg-gray-100 text-black"
127 | }`;
128 | const isBracketedMessage =
129 | title.startsWith("[") && title.endsWith("]");
130 | const messageStyle = isBracketedMessage
131 | ? "italic text-gray-400"
132 | : "";
133 | const displayTitle = isBracketedMessage
134 | ? title.slice(1, -1)
135 | : title;
136 |
137 | return (
138 |
139 |
140 |
145 |
150 | {timestamp}
151 |
152 |
153 | {displayTitle}
154 |
155 |
156 | {guardrailResult && (
157 |
158 |
159 |
160 | )}
161 |
162 |
163 | );
164 | } else if (type === "BREADCRUMB") {
165 | return (
166 |
170 |
{timestamp}
171 |
data && toggleTranscriptItemExpand(itemId)}
176 | >
177 | {data && (
178 |
183 | ▶
184 |
185 | )}
186 | {title}
187 |
188 | {expanded && data && (
189 |
190 |
191 | {JSON.stringify(data, null, 2)}
192 |
193 |
194 | )}
195 |
196 | );
197 | } else {
198 | // Fallback if type is neither MESSAGE nor BREADCRUMB
199 | return (
200 |
204 | Unknown item type: {type}{" "}
205 | {timestamp}
206 |
207 | );
208 | }
209 | })}
210 |