SarahXia0405 commited on
Commit
ce17eee
·
verified ·
1 Parent(s): a76f242

Update web/src/components/Message.tsx

Browse files
Files changed (1) hide show
  1. web/src/components/Message.tsx +20 -43
web/src/components/Message.tsx CHANGED
@@ -33,19 +33,17 @@ import { apiFeedback } from "../lib/api";
33
  interface MessageProps {
34
  key?: React.Key;
35
  message: MessageType;
36
- showSenderInfo?: boolean; // For group chat mode
37
- isFirstGreeting?: boolean; // Indicates if this is the first greeting message
38
- showNextButton?: boolean; // For quiz mode
39
- onNextQuestion?: () => void; // For quiz mode
40
- chatMode?: "ask" | "review" | "quiz"; // Current chat mode
41
 
42
- // ✅ NEW: for feedback submission
43
  currentUserId?: string;
44
  docType?: string;
45
  learningMode?: string;
46
  }
47
 
48
- // 反馈标签选项
49
  const FEEDBACK_TAGS = {
50
  "not-helpful": [
51
  "Code was incorrect",
@@ -63,7 +61,6 @@ const FEEDBACK_TAGS = {
63
  ],
64
  };
65
 
66
- // ---- Markdown list normalization ----
67
  function normalizeMarkdownLists(input: string) {
68
  if (!input) return input;
69
 
@@ -79,10 +76,8 @@ export function Message({
79
  showNextButton = false,
80
  onNextQuestion,
81
  chatMode = "ask",
82
-
83
- // ✅ NEW
84
  currentUserId,
85
- docType = "Syllabus",
86
  learningMode = "general",
87
  }: MessageProps) {
88
  const [feedback, setFeedback] = useState<"helpful" | "not-helpful" | null>(
@@ -90,7 +85,6 @@ export function Message({
90
  );
91
  const [copied, setCopied] = useState(false);
92
 
93
- // ✅ References UI state
94
  const [referencesOpen, setReferencesOpen] = useState(false);
95
 
96
  const [showFeedbackArea, setShowFeedbackArea] = useState(false);
@@ -106,6 +100,9 @@ export function Message({
106
  isFirstGreeting || message.id === "review-1" || message.id === "quiz-1";
107
  const shouldShowActions = isUser ? true : !isWelcomeMessage;
108
 
 
 
 
109
  const handleCopy = async () => {
110
  await navigator.clipboard.writeText(message.content);
111
  setCopied(true);
@@ -140,7 +137,6 @@ export function Message({
140
  );
141
  };
142
 
143
- // ✅ REAL submit to backend -> LangSmith dataset
144
  const handleFeedbackSubmit = async () => {
145
  if (!currentUserId || !currentUserId.trim()) {
146
  toast.error("Missing user_id; cannot submit feedback.");
@@ -151,7 +147,6 @@ export function Message({
151
  return;
152
  }
153
 
154
- // UI uses "not-helpful" (dash), backend expects "not_helpful" (underscore)
155
  const rating = feedbackType === "helpful" ? "helpful" : "not_helpful";
156
 
157
  try {
@@ -160,7 +155,7 @@ export function Message({
160
  rating,
161
  assistant_message_id: message.id,
162
  assistant_text: message.content,
163
- user_text: "", // optional
164
  comment: feedbackText || "",
165
  tags: selectedTags,
166
  refs: message.references ?? [],
@@ -242,18 +237,12 @@ export function Message({
242
  );
243
  };
244
 
245
- const refsArray = Array.isArray(message.references) ? message.references : [];
246
- const refsCount = refsArray.length;
247
-
248
  const attachments = (message as any).attachments as
249
  | Array<{ name: string; kind: string; size: number; fileType?: string }>
250
  | undefined;
251
 
252
  const hasAttachments = !!(attachments && attachments.length);
253
 
254
- // ✅ show references row for any assistant message (even 0), but not for user.
255
- const shouldShowReferencesRow = !isUser;
256
-
257
  return (
258
  <div
259
  className={`flex gap-2 ${
@@ -284,11 +273,7 @@ export function Message({
284
  </div>
285
  ) : !isUser ? (
286
  <div className="w-10 h-10 rounded-full overflow-hidden bg-white flex items-center justify-center flex-shrink-0">
287
- <img
288
- src={clareAvatar}
289
- alt="Clare"
290
- className="w-full h-full object-cover"
291
- />
292
  </div>
293
  ) : null}
294
 
@@ -298,7 +283,6 @@ export function Message({
298
  }`}
299
  style={{ maxWidth: "min(770px, calc(100% - 2rem))" }}
300
  >
301
- {/* Sender name in group chat */}
302
  {showSenderInfo && message.sender && (
303
  <div className="flex items-center gap-2 px-1">
304
  <span className="text-xs">{message.sender.name}</span>
@@ -313,20 +297,17 @@ export function Message({
313
  {/* Bubble */}
314
  <div
315
  className={`
 
316
  rounded-2xl px-4 py-3
317
  ${isUser && !showSenderInfo ? "bg-primary text-primary-foreground" : "bg-muted"}
318
  `}
319
  >
320
- {/* Attachments shown “with” the message (neutral card style) */}
321
  {hasAttachments && (
322
  <div className="mb-3 flex flex-wrap gap-2">
323
  {attachments!.map((a, idx) => {
324
  const icon =
325
- a.kind === "pdf"
326
- ? pdfIcon
327
- : a.kind === "ppt"
328
- ? pptIcon
329
- : otherIcon;
330
 
331
  const label =
332
  a.kind === "pdf"
@@ -376,9 +357,9 @@ export function Message({
376
  {renderBubbleContent()}
377
  </div>
378
 
379
- {/* ✅ References row: BETWEEN bubble and actions */}
380
- {shouldShowReferencesRow && (
381
- <div className="mt-1">
382
  <Collapsible open={referencesOpen} onOpenChange={setReferencesOpen}>
383
  <CollapsibleTrigger asChild>
384
  <button
@@ -393,7 +374,7 @@ export function Message({
393
  hover:bg-background
394
  transition
395
  "
396
- title="References"
397
  >
398
  {referencesOpen ? (
399
  <ChevronUp className="h-3 w-3" />
@@ -411,7 +392,7 @@ export function Message({
411
  No references returned.
412
  </div>
413
  ) : (
414
- refsArray.map((ref, index) => (
415
  <div
416
  key={index}
417
  className="rounded-md border border-border bg-background/60 dark:bg-background/20 px-2 py-1 text-xs"
@@ -458,11 +439,7 @@ export function Message({
458
  onClick={handleCopy}
459
  title="Copy"
460
  >
461
- {copied ? (
462
- <Check className="h-4 w-4" />
463
- ) : (
464
- <Copy className="h-4 w-4" />
465
- )}
466
  </Button>
467
 
468
  {!isUser && (
 
33
  interface MessageProps {
34
  key?: React.Key;
35
  message: MessageType;
36
+ showSenderInfo?: boolean;
37
+ isFirstGreeting?: boolean;
38
+ showNextButton?: boolean;
39
+ onNextQuestion?: () => void;
40
+ chatMode?: "ask" | "review" | "quiz";
41
 
 
42
  currentUserId?: string;
43
  docType?: string;
44
  learningMode?: string;
45
  }
46
 
 
47
  const FEEDBACK_TAGS = {
48
  "not-helpful": [
49
  "Code was incorrect",
 
61
  ],
62
  };
63
 
 
64
  function normalizeMarkdownLists(input: string) {
65
  if (!input) return input;
66
 
 
76
  showNextButton = false,
77
  onNextQuestion,
78
  chatMode = "ask",
 
 
79
  currentUserId,
80
+ docType = "All",
81
  learningMode = "general",
82
  }: MessageProps) {
83
  const [feedback, setFeedback] = useState<"helpful" | "not-helpful" | null>(
 
85
  );
86
  const [copied, setCopied] = useState(false);
87
 
 
88
  const [referencesOpen, setReferencesOpen] = useState(false);
89
 
90
  const [showFeedbackArea, setShowFeedbackArea] = useState(false);
 
100
  isFirstGreeting || message.id === "review-1" || message.id === "quiz-1";
101
  const shouldShowActions = isUser ? true : !isWelcomeMessage;
102
 
103
+ const refsList = (message.references ?? []) as string[];
104
+ const refsCount = refsList.length;
105
+
106
  const handleCopy = async () => {
107
  await navigator.clipboard.writeText(message.content);
108
  setCopied(true);
 
137
  );
138
  };
139
 
 
140
  const handleFeedbackSubmit = async () => {
141
  if (!currentUserId || !currentUserId.trim()) {
142
  toast.error("Missing user_id; cannot submit feedback.");
 
147
  return;
148
  }
149
 
 
150
  const rating = feedbackType === "helpful" ? "helpful" : "not_helpful";
151
 
152
  try {
 
155
  rating,
156
  assistant_message_id: message.id,
157
  assistant_text: message.content,
158
+ user_text: "",
159
  comment: feedbackText || "",
160
  tags: selectedTags,
161
  refs: message.references ?? [],
 
237
  );
238
  };
239
 
 
 
 
240
  const attachments = (message as any).attachments as
241
  | Array<{ name: string; kind: string; size: number; fileType?: string }>
242
  | undefined;
243
 
244
  const hasAttachments = !!(attachments && attachments.length);
245
 
 
 
 
246
  return (
247
  <div
248
  className={`flex gap-2 ${
 
273
  </div>
274
  ) : !isUser ? (
275
  <div className="w-10 h-10 rounded-full overflow-hidden bg-white flex items-center justify-center flex-shrink-0">
276
+ <img src={clareAvatar} alt="Clare" className="w-full h-full object-cover" />
 
 
 
 
277
  </div>
278
  ) : null}
279
 
 
283
  }`}
284
  style={{ maxWidth: "min(770px, calc(100% - 2rem))" }}
285
  >
 
286
  {showSenderInfo && message.sender && (
287
  <div className="flex items-center gap-2 px-1">
288
  <span className="text-xs">{message.sender.name}</span>
 
297
  {/* Bubble */}
298
  <div
299
  className={`
300
+ relative
301
  rounded-2xl px-4 py-3
302
  ${isUser && !showSenderInfo ? "bg-primary text-primary-foreground" : "bg-muted"}
303
  `}
304
  >
305
+ {/* Attachments */}
306
  {hasAttachments && (
307
  <div className="mb-3 flex flex-wrap gap-2">
308
  {attachments!.map((a, idx) => {
309
  const icon =
310
+ a.kind === "pdf" ? pdfIcon : a.kind === "ppt" ? pptIcon : otherIcon;
 
 
 
 
311
 
312
  const label =
313
  a.kind === "pdf"
 
357
  {renderBubbleContent()}
358
  </div>
359
 
360
+ {/* ✅ References OUTSIDE bubble, BETWEEN bubble and actions */}
361
+ {!isUser && (
362
+ <div className="mt-0.5">
363
  <Collapsible open={referencesOpen} onOpenChange={setReferencesOpen}>
364
  <CollapsibleTrigger asChild>
365
  <button
 
374
  hover:bg-background
375
  transition
376
  "
377
+ title={refsCount > 0 ? "References" : "No references returned."}
378
  >
379
  {referencesOpen ? (
380
  <ChevronUp className="h-3 w-3" />
 
392
  No references returned.
393
  </div>
394
  ) : (
395
+ refsList.map((ref, index) => (
396
  <div
397
  key={index}
398
  className="rounded-md border border-border bg-background/60 dark:bg-background/20 px-2 py-1 text-xs"
 
439
  onClick={handleCopy}
440
  title="Copy"
441
  >
442
+ {copied ? <Check className="h-4 w-4" /> : <Copy className="h-4 w-4" />}
 
 
 
 
443
  </Button>
444
 
445
  {!isUser && (