SarahXia0405 commited on
Commit
66ffb7c
·
verified ·
1 Parent(s): f36fc08

Update web/src/components/LeftSidebar.tsx

Browse files
Files changed (1) hide show
  1. web/src/components/LeftSidebar.tsx +44 -12
web/src/components/LeftSidebar.tsx CHANGED
@@ -15,7 +15,9 @@ import { jsPDF } from "jspdf";
15
  import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./ui/dialog";
16
  import type { CourseInfo } from "../App";
17
 
18
- // Saved Chat Item Component with rename functionality
 
 
19
  function SavedChatItem({
20
  chat,
21
  onLoadChat,
@@ -69,10 +71,7 @@ function SavedChatItem({
69
 
70
  const handleInputBlur = (e: React.FocusEvent<HTMLInputElement>) => {
71
  const relatedTarget = e.relatedTarget as HTMLElement;
72
- if (
73
- relatedTarget &&
74
- (cancelButtonRef.current?.contains(relatedTarget) || saveButtonRef.current?.contains(relatedTarget))
75
- ) {
76
  return;
77
  }
78
  if (editTitle.trim() && editTitle !== originalTitle && onRenameSavedChat) {
@@ -166,7 +165,13 @@ function SavedChatItem({
166
  ) : (
167
  <>
168
  {onRenameSavedChat && (
169
- <Button variant="ghost" size="icon" className="h-5 w-5 flex-shrink-0 hover:bg-muted" onClick={handleStartEdit} title="Rename chat">
 
 
 
 
 
 
170
  <Edit2 className="h-3 w-3" />
171
  </Button>
172
  )}
@@ -270,6 +275,29 @@ export function LeftSidebar({
270
  const [isDownloading, setIsDownloading] = useState(false);
271
  const [copied, setCopied] = useState(false);
272
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  const handleLogin = () => {
274
  if (!name.trim() || !email.trim()) {
275
  toast.error("Please fill in all fields");
@@ -424,8 +452,7 @@ export function LeftSidebar({
424
  }
425
  };
426
 
427
- const filteredSavedItems = savedItems
428
- .filter((item) => item.workspaceId === currentWorkspaceId);
429
 
430
  const defaultCourses = [
431
  { id: "course1", name: "Introduction to AI" },
@@ -494,6 +521,7 @@ export function LeftSidebar({
494
  const courseDisplayInfo = getCourseDisplayInfo();
495
 
496
  return (
 
497
  <div className="h-full min-h-0 flex flex-col overflow-hidden">
498
  {/* Top fixed blocks */}
499
  {isLoggedIn && courseDisplayInfo && (
@@ -610,14 +638,18 @@ export function LeftSidebar({
610
  </div>
611
  )}
612
 
613
- {/* Saved Chat: ONLY this list scrolls */}
614
  {isLoggedIn && (
615
- <div className="flex-1 min-h-0 flex flex-col">
616
  <div className="p-4 border-b border-border flex-shrink-0">
617
  <h3 className="text-base font-medium">Saved Chat</h3>
618
  </div>
619
 
620
- <div className="flex-1 min-h-0 overflow-y-auto overscroll-contain p-4">
 
 
 
 
621
  {savedChats.length === 0 ? (
622
  <div className="text-sm text-muted-foreground text-center py-4">
623
  <MessageSquare className="h-8 w-8 mx-auto mb-2 opacity-50" />
@@ -641,7 +673,7 @@ export function LeftSidebar({
641
  </div>
642
  )}
643
 
644
- {/* Saved Item Dialog (保持你原逻辑) */}
645
  <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
646
  <DialogContent className="max-w-4xl max-h-[85vh] flex flex-col">
647
  <DialogHeader>
 
15
  import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./ui/dialog";
16
  import type { CourseInfo } from "../App";
17
 
18
+ // ================================
19
+ // Saved Chat Item (unchanged)
20
+ // ================================
21
  function SavedChatItem({
22
  chat,
23
  onLoadChat,
 
71
 
72
  const handleInputBlur = (e: React.FocusEvent<HTMLInputElement>) => {
73
  const relatedTarget = e.relatedTarget as HTMLElement;
74
+ if (relatedTarget && (cancelButtonRef.current?.contains(relatedTarget) || saveButtonRef.current?.contains(relatedTarget))) {
 
 
 
75
  return;
76
  }
77
  if (editTitle.trim() && editTitle !== originalTitle && onRenameSavedChat) {
 
165
  ) : (
166
  <>
167
  {onRenameSavedChat && (
168
+ <Button
169
+ variant="ghost"
170
+ size="icon"
171
+ className="h-5 w-5 flex-shrink-0 hover:bg-muted"
172
+ onClick={handleStartEdit}
173
+ title="Rename chat"
174
+ >
175
  <Edit2 className="h-3 w-3" />
176
  </Button>
177
  )}
 
275
  const [isDownloading, setIsDownloading] = useState(false);
276
  const [copied, setCopied] = useState(false);
277
 
278
+ // ✅ NEW: ref for Saved scroll area
279
+ const savedScrollRef = useRef<HTMLDivElement>(null);
280
+
281
+ // ✅ NEW: hard-stop scroll chaining from Saved area to outer containers
282
+ useEffect(() => {
283
+ const el = savedScrollRef.current;
284
+ if (!el) return;
285
+
286
+ const onWheel = (e: WheelEvent) => {
287
+ e.stopPropagation();
288
+
289
+ const atTop = el.scrollTop <= 0;
290
+ const atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 1;
291
+
292
+ if ((atTop && e.deltaY < 0) || (atBottom && e.deltaY > 0)) {
293
+ e.preventDefault();
294
+ }
295
+ };
296
+
297
+ el.addEventListener("wheel", onWheel, { passive: false });
298
+ return () => el.removeEventListener("wheel", onWheel);
299
+ }, []);
300
+
301
  const handleLogin = () => {
302
  if (!name.trim() || !email.trim()) {
303
  toast.error("Please fill in all fields");
 
452
  }
453
  };
454
 
455
+ const filteredSavedItems = savedItems.filter((item) => item.workspaceId === currentWorkspaceId);
 
456
 
457
  const defaultCourses = [
458
  { id: "course1", name: "Introduction to AI" },
 
521
  const courseDisplayInfo = getCourseDisplayInfo();
522
 
523
  return (
524
+ // ✅ LeftSidebar itself never scrolls. Only the Saved list scrolls.
525
  <div className="h-full min-h-0 flex flex-col overflow-hidden">
526
  {/* Top fixed blocks */}
527
  {isLoggedIn && courseDisplayInfo && (
 
638
  </div>
639
  )}
640
 
641
+ {/* Saved Chat: ONLY this list scrolls */}
642
  {isLoggedIn && (
643
+ <div className="flex-1 min-h-0 flex flex-col overflow-hidden">
644
  <div className="p-4 border-b border-border flex-shrink-0">
645
  <h3 className="text-base font-medium">Saved Chat</h3>
646
  </div>
647
 
648
+ <div
649
+ ref={savedScrollRef}
650
+ className="flex-1 min-h-0 overflow-y-auto p-4"
651
+ style={{ overscrollBehavior: "contain" }}
652
+ >
653
  {savedChats.length === 0 ? (
654
  <div className="text-sm text-muted-foreground text-center py-4">
655
  <MessageSquare className="h-8 w-8 mx-auto mb-2 opacity-50" />
 
673
  </div>
674
  )}
675
 
676
+ {/* Saved Item Dialog (keep your logic) */}
677
  <Dialog open={isDialogOpen} onOpenChange={setIsDialogOpen}>
678
  <DialogContent className="max-w-4xl max-h-[85vh] flex flex-col">
679
  <DialogHeader>