SarahXia0405 commited on
Commit
3f142c3
·
verified ·
1 Parent(s): b976f1b

Update web/src/components/Message.tsx

Browse files
Files changed (1) hide show
  1. web/src/components/Message.tsx +20 -45
web/src/components/Message.tsx CHANGED
@@ -4,15 +4,7 @@ import { Button } from "./ui/button";
4
  import ReactMarkdown from "react-markdown";
5
  import remarkGfm from "remark-gfm";
6
 
7
- import {
8
- Copy,
9
- ThumbsUp,
10
- ThumbsDown,
11
- ChevronDown,
12
- ChevronUp,
13
- Check,
14
- X,
15
- } from "lucide-react";
16
  import { Badge } from "./ui/badge";
17
  import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./ui/collapsible";
18
  import { Textarea } from "./ui/textarea";
@@ -23,14 +15,13 @@ import clareAvatar from "../assets/dfe44dab3ad8cd93953eac4a3e68bd1a5f999653.png"
23
  interface MessageProps {
24
  key?: React.Key;
25
  message: MessageType;
26
- showSenderInfo?: boolean; // For group chat mode
27
- isFirstGreeting?: boolean; // Indicates if this is the first greeting message
28
- showNextButton?: boolean; // For quiz mode
29
- onNextQuestion?: () => void; // For quiz mode
30
- chatMode?: "ask" | "review" | "quiz"; // Current chat mode
31
  }
32
 
33
- // 反馈标签选项
34
  const FEEDBACK_TAGS = {
35
  "not-helpful": [
36
  "Code was incorrect",
@@ -42,6 +33,14 @@ const FEEDBACK_TAGS = {
42
  helpful: ["Accurate and helpful", "Clear explanation", "Good examples", "Solved my problem", "Well structured"],
43
  };
44
 
 
 
 
 
 
 
 
 
45
  export function Message({
46
  message,
47
  showSenderInfo = false,
@@ -89,7 +88,6 @@ export function Message({
89
  setFeedbackType(null);
90
  setFeedbackText("");
91
  setSelectedTags([]);
92
- // 不重置 feedback,保持点赞/点踩的状态
93
  };
94
 
95
  const handleTagToggle = (tag: string) => {
@@ -103,25 +101,16 @@ export function Message({
103
  text: feedbackText,
104
  messageId: message.id || message.content.substring(0, 50),
105
  };
106
-
107
  console.log("Feedback submitted:", feedbackData);
108
  toast.success("感谢您的反馈!");
109
  handleFeedbackClose();
110
  };
111
 
112
- // ✅ Markdown renderer (only for assistant; user keeps plain text)
113
  const markdownClass = useMemo(() => {
114
- // 这里用 prose 让列表/加粗/斜体更自然;并控制尺寸不要太夸张
115
- // 注意:Tailwind prose 需要你项目里已启用 typography 插件;如果你没启用,也不会报错,只是 class 不生效
116
- // 即便 prose 不生效,下面 components 也能保证基本样式
117
- const base =
118
- "text-base leading-relaxed " +
119
- "prose prose-sm max-w-none " +
120
- "prose-p:my-2 prose-ul:my-2 prose-ol:my-2 prose-li:my-1 " +
121
- "prose-strong:font-semibold prose-code:px-1 prose-code:py-0.5 prose-code:rounded " +
122
- "prose-pre:p-3 prose-pre:rounded-lg " +
123
- "whitespace-pre-wrap break-words";
124
- return base;
125
  }, []);
126
 
127
  const renderBubbleContent = () => {
@@ -134,7 +123,6 @@ export function Message({
134
  remarkPlugins={[remarkGfm]}
135
  className={markdownClass}
136
  components={{
137
- // 保证段落不要把气泡撑得很松
138
  p: ({ children, ...props }) => (
139
  <p className="my-2 whitespace-pre-wrap break-words" {...props}>
140
  {children}
@@ -165,7 +153,6 @@ export function Message({
165
  {children}
166
  </em>
167
  ),
168
- // 避免 code 把气泡撑开
169
  code: ({ children, ...props }) => (
170
  <code className="px-1 py-0.5 rounded bg-black/5 dark:bg-white/10" {...props}>
171
  {children}
@@ -176,7 +163,6 @@ export function Message({
176
  {children}
177
  </pre>
178
  ),
179
- // 你如果不想自动生成链接样式,可以在这里控制
180
  a: ({ children, ...props }) => (
181
  <a className="underline underline-offset-2" target="_blank" rel="noreferrer" {...props}>
182
  {children}
@@ -184,7 +170,7 @@ export function Message({
184
  ),
185
  }}
186
  >
187
- {message.content}
188
  </ReactMarkdown>
189
  );
190
  };
@@ -219,7 +205,6 @@ export function Message({
219
  className={`group flex flex-col gap-2 ${isUser && !showSenderInfo ? "items-end" : "items-start"}`}
220
  style={{ maxWidth: "min(770px, calc(100% - 2rem))" }}
221
  >
222
- {/* Sender name in group chat */}
223
  {showSenderInfo && message.sender && (
224
  <div className="flex items-center gap-2 px-1">
225
  <span className="text-xs">{message.sender.name}</span>
@@ -227,17 +212,10 @@ export function Message({
227
  </div>
228
  )}
229
 
230
- {/* Bubble */}
231
- <div
232
- className={`
233
- rounded-2xl px-4 py-3
234
- ${isUser && !showSenderInfo ? "bg-primary text-primary-foreground" : "bg-muted"}
235
- `}
236
- >
237
  {renderBubbleContent()}
238
  </div>
239
 
240
- {/* Next Question Button for Quiz Mode */}
241
  {!isUser && showNextButton && !nextButtonClicked && chatMode === "quiz" && onNextQuestion && (
242
  <div className="mt-2">
243
  <Button
@@ -252,7 +230,6 @@ export function Message({
252
  </div>
253
  )}
254
 
255
- {/* References */}
256
  {message.references && message.references.length > 0 && (
257
  <Collapsible open={referencesOpen} onOpenChange={setReferencesOpen}>
258
  <CollapsibleTrigger asChild>
@@ -271,7 +248,6 @@ export function Message({
271
  </Collapsible>
272
  )}
273
 
274
- {/* Message Actions */}
275
  {shouldShowActions && (
276
  <div className="flex items-center gap-1">
277
  <Button
@@ -315,7 +291,6 @@ export function Message({
315
  </div>
316
  )}
317
 
318
- {/* Feedback Area */}
319
  {!isUser && showFeedbackArea && feedbackType && (
320
  <div className="w-full mt-2 bg-gray-50 dark:bg-gray-800/50 rounded-lg p-4 border border-gray-200 dark:border-gray-700">
321
  <div className="flex items-start justify-between mb-4">
 
4
  import ReactMarkdown from "react-markdown";
5
  import remarkGfm from "remark-gfm";
6
 
7
+ import { Copy, ThumbsUp, ThumbsDown, ChevronDown, ChevronUp, Check, X } from "lucide-react";
 
 
 
 
 
 
 
 
8
  import { Badge } from "./ui/badge";
9
  import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./ui/collapsible";
10
  import { Textarea } from "./ui/textarea";
 
15
  interface MessageProps {
16
  key?: React.Key;
17
  message: MessageType;
18
+ showSenderInfo?: boolean;
19
+ isFirstGreeting?: boolean;
20
+ showNextButton?: boolean;
21
+ onNextQuestion?: () => void;
22
+ chatMode?: "ask" | "review" | "quiz";
23
  }
24
 
 
25
  const FEEDBACK_TAGS = {
26
  "not-helpful": [
27
  "Code was incorrect",
 
33
  helpful: ["Accurate and helpful", "Clear explanation", "Good examples", "Solved my problem", "Well structured"],
34
  };
35
 
36
+ function unescapeMarkdown(s: string) {
37
+ // 处理最常见的“被转义导致 Markdown 不生效”的情况:\*\*bold\*\*
38
+ return (s || "")
39
+ .replace(/\\\*/g, "*")
40
+ .replace(/\\_/g, "_")
41
+ .replace(/\\`/g, "`");
42
+ }
43
+
44
  export function Message({
45
  message,
46
  showSenderInfo = false,
 
88
  setFeedbackType(null);
89
  setFeedbackText("");
90
  setSelectedTags([]);
 
91
  };
92
 
93
  const handleTagToggle = (tag: string) => {
 
101
  text: feedbackText,
102
  messageId: message.id || message.content.substring(0, 50),
103
  };
 
104
  console.log("Feedback submitted:", feedbackData);
105
  toast.success("感谢您的反馈!");
106
  handleFeedbackClose();
107
  };
108
 
 
109
  const markdownClass = useMemo(() => {
110
+ return (
111
+ "text-base leading-relaxed max-w-none " +
112
+ "whitespace-pre-wrap break-words"
113
+ );
 
 
 
 
 
 
 
114
  }, []);
115
 
116
  const renderBubbleContent = () => {
 
123
  remarkPlugins={[remarkGfm]}
124
  className={markdownClass}
125
  components={{
 
126
  p: ({ children, ...props }) => (
127
  <p className="my-2 whitespace-pre-wrap break-words" {...props}>
128
  {children}
 
153
  {children}
154
  </em>
155
  ),
 
156
  code: ({ children, ...props }) => (
157
  <code className="px-1 py-0.5 rounded bg-black/5 dark:bg-white/10" {...props}>
158
  {children}
 
163
  {children}
164
  </pre>
165
  ),
 
166
  a: ({ children, ...props }) => (
167
  <a className="underline underline-offset-2" target="_blank" rel="noreferrer" {...props}>
168
  {children}
 
170
  ),
171
  }}
172
  >
173
+ {unescapeMarkdown(message.content)}
174
  </ReactMarkdown>
175
  );
176
  };
 
205
  className={`group flex flex-col gap-2 ${isUser && !showSenderInfo ? "items-end" : "items-start"}`}
206
  style={{ maxWidth: "min(770px, calc(100% - 2rem))" }}
207
  >
 
208
  {showSenderInfo && message.sender && (
209
  <div className="flex items-center gap-2 px-1">
210
  <span className="text-xs">{message.sender.name}</span>
 
212
  </div>
213
  )}
214
 
215
+ <div className={`rounded-2xl px-4 py-3 ${isUser && !showSenderInfo ? "bg-primary text-primary-foreground" : "bg-muted"}`}>
 
 
 
 
 
 
216
  {renderBubbleContent()}
217
  </div>
218
 
 
219
  {!isUser && showNextButton && !nextButtonClicked && chatMode === "quiz" && onNextQuestion && (
220
  <div className="mt-2">
221
  <Button
 
230
  </div>
231
  )}
232
 
 
233
  {message.references && message.references.length > 0 && (
234
  <Collapsible open={referencesOpen} onOpenChange={setReferencesOpen}>
235
  <CollapsibleTrigger asChild>
 
248
  </Collapsible>
249
  )}
250
 
 
251
  {shouldShowActions && (
252
  <div className="flex items-center gap-1">
253
  <Button
 
291
  </div>
292
  )}
293
 
 
294
  {!isUser && showFeedbackArea && feedbackType && (
295
  <div className="w-full mt-2 bg-gray-50 dark:bg-gray-800/50 rounded-lg p-4 border border-gray-200 dark:border-gray-700">
296
  <div className="flex items-start justify-between mb-4">