SarahXia0405 commited on
Commit
cb577c7
·
verified ·
1 Parent(s): acdaba5

Update web/src/components/Message.tsx

Browse files
Files changed (1) hide show
  1. web/src/components/Message.tsx +64 -16
web/src/components/Message.tsx CHANGED
@@ -64,11 +64,35 @@ const FEEDBACK_TAGS = {
64
  function normalizeMarkdownLists(input: string) {
65
  if (!input) return input;
66
 
67
- return (
68
- input
69
- .replace(/(^|\n)(\s*)(\d+\.)\s*\n+\s+/g, "$1$2$3 ")
70
- .replace(/(^|\n)(\s*)([-*+])\s*\n+\s+/g, "$1$2$3 ")
71
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  }
73
 
74
  export function Message({
@@ -150,6 +174,9 @@ export function Message({
150
  // UI uses "not-helpful" (dash), backend expects "not_helpful" (underscore)
151
  const rating = feedbackType === "helpful" ? "helpful" : "not_helpful";
152
 
 
 
 
153
  try {
154
  await apiFeedback({
155
  user_id: currentUserId,
@@ -159,7 +186,7 @@ export function Message({
159
  user_text: "", // optional; you can wire last user msg later if you want
160
  comment: feedbackText || "",
161
  tags: selectedTags,
162
- refs: message.references ?? [],
163
  learning_mode: chatMode === "ask" ? learningMode : chatMode,
164
  doc_type: docType,
165
  timestamp_ms: Date.now(),
@@ -207,7 +234,9 @@ export function Message({
207
  p: ({ children }) => (
208
  <p className="my-2 whitespace-pre-wrap break-words">{children}</p>
209
  ),
210
- ul: ({ children }) => <ul className="my-3 pl-6 space-y-2">{children}</ul>,
 
 
211
  ol: ({ children }) => <ol className="my-3 space-y-4">{children}</ol>,
212
  li: ({ children, node }) => {
213
  const parent = (node as any)?.parent?.tagName;
@@ -225,7 +254,9 @@ export function Message({
225
  }
226
  return <li>{children}</li>;
227
  },
228
- strong: ({ children }) => <strong className="font-semibold">{children}</strong>,
 
 
229
  em: ({ children }) => <em className="italic">{children}</em>,
230
  }}
231
  >
@@ -234,6 +265,11 @@ export function Message({
234
  );
235
  };
236
 
 
 
 
 
 
237
  return (
238
  <div
239
  className={`flex gap-2 ${
@@ -244,7 +280,11 @@ export function Message({
244
  {showSenderInfo && message.sender ? (
245
  <div className="w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 overflow-hidden bg-white">
246
  {message.sender.isAI ? (
247
- <img src={clareAvatar} alt="Clare" className="w-full h-full object-cover" />
 
 
 
 
248
  ) : (
249
  <img
250
  src={
@@ -311,18 +351,22 @@ export function Message({
311
  </div>
312
  )}
313
 
314
- {/* References */}
315
- {message.references && message.references.length > 0 && (
316
  <Collapsible open={referencesOpen} onOpenChange={setReferencesOpen}>
317
  <CollapsibleTrigger asChild>
318
  <Button variant="ghost" size="sm" className="gap-1 h-7 text-xs">
319
- {referencesOpen ? <ChevronUp className="h-3 w-3" /> : <ChevronDown className="h-3 w-3" />}
320
- {message.references.length}{" "}
321
- {message.references.length === 1 ? "reference" : "references"}
 
 
 
 
322
  </Button>
323
  </CollapsibleTrigger>
324
  <CollapsibleContent className="space-y-1 mt-1">
325
- {message.references.map((ref, index) => (
326
  <Badge key={index} variant="outline" className="text-xs">
327
  {ref}
328
  </Badge>
@@ -345,7 +389,11 @@ export function Message({
345
  onClick={handleCopy}
346
  title="Copy"
347
  >
348
- {copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
 
 
 
 
349
  </Button>
350
 
351
  {!isUser && (
 
64
  function normalizeMarkdownLists(input: string) {
65
  if (!input) return input;
66
 
67
+ return input
68
+ .replace(/(^|\n)(\s*)(\d+\.)\s*\n+\s+/g, "$1$2$3 ")
69
+ .replace(/(^|\n)(\s*)([-*+])\s*\n+\s+/g, "$1$2$3 ");
70
+ }
71
+
72
+ // ✅ References normalization: supports string[] OR {source_file, section}[]
73
+ type RefObj = { source_file?: string | null; section?: string | null };
74
+ type RefItem = string | RefObj;
75
+
76
+ function normalizeRefs(raw: any): string[] {
77
+ const arr: RefItem[] = Array.isArray(raw) ? raw : [];
78
+ const out: string[] = [];
79
+
80
+ for (const r of arr) {
81
+ if (typeof r === "string") {
82
+ const t = r.trim();
83
+ if (t) out.push(t);
84
+ continue;
85
+ }
86
+
87
+ if (r && typeof r === "object") {
88
+ const file = String((r as RefObj).source_file ?? "").trim();
89
+ const sec = String((r as RefObj).section ?? "").trim();
90
+ const text = file && sec ? `${file} — ${sec}` : file || sec;
91
+ if (text) out.push(text);
92
+ }
93
+ }
94
+
95
+ return out;
96
  }
97
 
98
  export function Message({
 
174
  // UI uses "not-helpful" (dash), backend expects "not_helpful" (underscore)
175
  const rating = feedbackType === "helpful" ? "helpful" : "not_helpful";
176
 
177
+ // ✅ normalize refs to string[] to match backend schema
178
+ const normalizedRefs = normalizeRefs((message as any).references);
179
+
180
  try {
181
  await apiFeedback({
182
  user_id: currentUserId,
 
186
  user_text: "", // optional; you can wire last user msg later if you want
187
  comment: feedbackText || "",
188
  tags: selectedTags,
189
+ refs: normalizedRefs,
190
  learning_mode: chatMode === "ask" ? learningMode : chatMode,
191
  doc_type: docType,
192
  timestamp_ms: Date.now(),
 
234
  p: ({ children }) => (
235
  <p className="my-2 whitespace-pre-wrap break-words">{children}</p>
236
  ),
237
+ ul: ({ children }) => (
238
+ <ul className="my-3 pl-6 space-y-2">{children}</ul>
239
+ ),
240
  ol: ({ children }) => <ol className="my-3 space-y-4">{children}</ol>,
241
  li: ({ children, node }) => {
242
  const parent = (node as any)?.parent?.tagName;
 
254
  }
255
  return <li>{children}</li>;
256
  },
257
+ strong: ({ children }) => (
258
+ <strong className="font-semibold">{children}</strong>
259
+ ),
260
  em: ({ children }) => <em className="italic">{children}</em>,
261
  }}
262
  >
 
265
  );
266
  };
267
 
268
+ // ✅ normalize references for UI display as well
269
+ const displayRefs = useMemo(() => {
270
+ return normalizeRefs((message as any).references);
271
+ }, [message]);
272
+
273
  return (
274
  <div
275
  className={`flex gap-2 ${
 
280
  {showSenderInfo && message.sender ? (
281
  <div className="w-10 h-10 rounded-full flex items-center justify-center flex-shrink-0 overflow-hidden bg-white">
282
  {message.sender.isAI ? (
283
+ <img
284
+ src={clareAvatar}
285
+ alt="Clare"
286
+ className="w-full h-full object-cover"
287
+ />
288
  ) : (
289
  <img
290
  src={
 
351
  </div>
352
  )}
353
 
354
+ {/* References (restored) */}
355
+ {displayRefs.length > 0 && (
356
  <Collapsible open={referencesOpen} onOpenChange={setReferencesOpen}>
357
  <CollapsibleTrigger asChild>
358
  <Button variant="ghost" size="sm" className="gap-1 h-7 text-xs">
359
+ {referencesOpen ? (
360
+ <ChevronUp className="h-3 w-3" />
361
+ ) : (
362
+ <ChevronDown className="h-3 w-3" />
363
+ )}
364
+ {displayRefs.length}{" "}
365
+ {displayRefs.length === 1 ? "reference" : "references"}
366
  </Button>
367
  </CollapsibleTrigger>
368
  <CollapsibleContent className="space-y-1 mt-1">
369
+ {displayRefs.map((ref, index) => (
370
  <Badge key={index} variant="outline" className="text-xs">
371
  {ref}
372
  </Badge>
 
389
  onClick={handleCopy}
390
  title="Copy"
391
  >
392
+ {copied ? (
393
+ <Check className="h-4 w-4" />
394
+ ) : (
395
+ <Copy className="h-4 w-4" />
396
+ )}
397
  </Button>
398
 
399
  {!isUser && (