aki-008 commited on
Commit
4a0902b
·
1 Parent(s): feafade

chore: notes ui update

Browse files
Files changed (1) hide show
  1. Frontend/src/pages/note.tsx +83 -59
Frontend/src/pages/note.tsx CHANGED
@@ -13,6 +13,7 @@ import {
13
  Trash2,
14
  Edit2,
15
  Check,
 
16
  } from "lucide-react";
17
  import {
18
  fetchNotes,
@@ -61,10 +62,8 @@ const Notes: React.FC = () => {
61
  loadNotes();
62
  }, []);
63
 
64
- // --- FIX: Robust Auto-Scroll ---
65
  const scrollToBottom = () => {
66
- // "smooth" gets interrupted by rapid streaming updates.
67
- // "instant" ensures it snaps to bottom every time a token arrives.
68
  messagesEndRef.current?.scrollIntoView({
69
  behavior: "instant",
70
  block: "end",
@@ -78,6 +77,7 @@ const Notes: React.FC = () => {
78
  // --- Resizing Logic ---
79
  const startResizing = useCallback(() => setIsResizing(true), []);
80
  const stopResizing = useCallback(() => setIsResizing(false), []);
 
81
  const resize = useCallback(
82
  (mouseMoveEvent: MouseEvent) => {
83
  if (isResizing) {
@@ -158,6 +158,7 @@ const Notes: React.FC = () => {
158
  const saveRename = async (e: React.MouseEvent) => {
159
  e.stopPropagation();
160
  if (!editingNoteId || !editName.trim()) return;
 
161
  try {
162
  await renameNote(editingNoteId, editName);
163
  setNotes((prev) =>
@@ -259,18 +260,23 @@ const Notes: React.FC = () => {
259
  };
260
 
261
  return (
262
- <div className="flex bg-black h-screen overflow-hidden">
263
- {/* --- Left Sidebar --- */}
 
264
  <div
265
- className={`h-screen shrink-0 transition-all duration-300 bg-gray-900 border-r border-gray-700 flex flex-col gap-4 ${
266
- isSidebarOpen ? "w-64 p-4" : "w-0 p-0 overflow-hidden"
267
  }`}
268
  >
269
  {isSidebarOpen && (
270
  <>
271
- <h3 className="text-xl font-bold text-white mb-2 border-b border-gray-700 pb-2">
272
- My Notes
273
- </h3>
 
 
 
 
274
  <input
275
  type="file"
276
  ref={fileInputRef}
@@ -278,10 +284,12 @@ const Notes: React.FC = () => {
278
  accept="application/pdf"
279
  onChange={handleFileChange}
280
  />
 
 
281
  <button
282
  onClick={handleUploadClick}
283
  disabled={isUploading}
284
- className="flex items-center gap-3 w-full bg-blue-600 text-white px-4 py-3 rounded-lg hover:bg-blue-700 transition font-semibold"
285
  >
286
  {isUploading ? (
287
  <Loader2 className="animate-spin" size={20} />
@@ -291,8 +299,8 @@ const Notes: React.FC = () => {
291
  {isUploading ? "Uploading..." : "Upload New PDF"}
292
  </button>
293
 
294
- <div className="mt-4 pt-4 border-t border-gray-700 space-y-2 overflow-y-auto">
295
- <p className="text-sm text-gray-400 uppercase tracking-wider">
296
  History
297
  </p>
298
 
@@ -300,10 +308,10 @@ const Notes: React.FC = () => {
300
  <div
301
  key={note.id}
302
  onClick={() => handleNoteSelect(note)}
303
- className={`group relative text-gray-200 p-3 rounded-md cursor-pointer flex items-center justify-between hover:bg-gray-700 transition ${
304
  currentNote?.id === note.id
305
- ? "bg-gray-800 border border-blue-500"
306
- : ""
307
  }`}
308
  >
309
  {editingNoteId === note.id ? (
@@ -313,44 +321,54 @@ const Notes: React.FC = () => {
313
  value={editName}
314
  onChange={(e) => setEditName(e.target.value)}
315
  onClick={(e) => e.stopPropagation()}
316
- className="flex-1 bg-gray-900 text-white text-xs px-2 py-1 rounded border border-blue-500 outline-none"
317
  autoFocus
318
  />
319
  <button
320
  onClick={saveRename}
321
- className="text-green-400 hover:text-green-300 p-1"
322
  >
323
  <Check size={14} />
324
  </button>
325
  <button
326
  onClick={cancelRename}
327
- className="text-red-400 hover:text-red-300 p-1"
328
  >
329
  <X size={14} />
330
  </button>
331
  </div>
332
  ) : (
333
  <>
334
- <div className="flex items-center gap-2 overflow-hidden">
335
  <FileText
336
  size={16}
337
- className="text-blue-400 shrink-0"
 
 
 
 
338
  />
339
- <span className="truncate text-sm">
 
 
 
 
 
 
340
  {note.filename}
341
  </span>
342
  </div>
343
  <div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
344
  <button
345
  onClick={(e) => startEditing(e, note)}
346
- className="p-1.5 hover:bg-gray-600 rounded text-gray-400 hover:text-white"
347
  title="Rename"
348
  >
349
  <Edit2 size={14} />
350
  </button>
351
  <button
352
  onClick={(e) => handleDeleteNote(e, note.id)}
353
- className="p-1.5 hover:bg-red-900/50 rounded text-gray-400 hover:text-red-400"
354
  title="Delete"
355
  >
356
  <Trash2 size={14} />
@@ -365,85 +383,90 @@ const Notes: React.FC = () => {
365
  )}
366
  </div>
367
 
368
- {/* --- Center: PDF Viewer --- */}
369
- <div className="flex flex-1 overflow-hidden relative flex-col">
370
- <header className="flex justify-between items-center p-4 bg-gray-900 border-b border-gray-700 shrink-0 z-10">
371
  <div className="flex items-center gap-3">
372
  <button
373
  onClick={() => setIsSidebarOpen(!isSidebarOpen)}
374
- className="p-2 rounded-lg bg-gray-800 hover:bg-gray-700 text-white transition"
375
  >
376
  <Menu size={20} />
377
  </button>
378
- <h2 className="text-lg font-semibold text-white truncate max-w-md">
379
  {currentNote ? currentNote.filename : "Select a Note"}
380
  </h2>
381
  </div>
382
  {!isChatOpen && currentNote && (
383
  <button
384
  onClick={() => setIsChatOpen(true)}
385
- className="flex items-center gap-2 bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-medium transition"
386
  >
387
  <MessageSquare size={18} /> Open Chat
388
  </button>
389
  )}
390
  </header>
391
 
392
- <div className="flex-1 w-full h-full bg-gray-800 relative">
393
  {pdfUrl ? (
394
- <iframe
395
- src={pdfUrl}
396
- className={`w-full h-full border-none ${
397
- isResizing ? "pointer-events-none" : ""
398
- }`}
399
- title="PDF Viewer"
400
- />
 
 
401
  ) : (
402
- <div className="flex flex-col items-center justify-center h-full text-gray-400">
403
- <FileText size={64} className="mb-4 opacity-50" />
404
- <p>Select a PDF from the sidebar to view</p>
 
 
405
  </div>
406
  )}
407
  </div>
408
  </div>
409
 
410
- {/* --- Resizable Chat Panel --- */}
411
  {isChatOpen && (
412
  <div
413
- className="w-1.5 hover:w-2 cursor-col-resize bg-gray-800 hover:bg-blue-500 transition-all z-20 flex items-center justify-center shrink-0"
414
  onMouseDown={startResizing}
415
  >
416
- <GripVertical size={16} className="text-gray-500" />
417
  </div>
418
  )}
419
 
420
  <div
421
  style={{ width: isChatOpen ? chatWidth : 0 }}
422
- className={`flex flex-col bg-gray-900 border-l border-gray-700 shrink-0 transition-all duration-100 ease-linear overflow-hidden`}
423
  >
424
  {isChatOpen && (
425
  <>
426
- <header className="flex justify-between items-center p-4 border-b border-gray-700 bg-gray-900 shrink-0">
427
  <div className="flex items-center gap-2">
428
- <MessageSquare size={18} className="text-blue-500" />
429
- <h3 className="text-lg font-bold text-white whitespace-nowrap">
430
  AI Chat
431
  </h3>
432
  </div>
433
  <button
434
  onClick={() => setIsChatOpen(false)}
435
- className="p-2 rounded-lg text-gray-400 hover:bg-gray-800 hover:text-white transition-colors"
436
  >
437
  <X size={20} />
438
  </button>
439
  </header>
440
 
441
- <div className="flex-1 overflow-y-auto p-4 space-y-4">
442
  {messages.length === 0 && (
443
- <div className="text-center text-gray-500 mt-10">
444
  <p>Ask a question about this document...</p>
445
  </div>
446
  )}
 
447
  {messages.map((msg, i) => (
448
  <div
449
  key={i}
@@ -452,10 +475,10 @@ const Notes: React.FC = () => {
452
  }`}
453
  >
454
  <div
455
- className={`max-w-[85%] p-3 rounded-lg text-sm ${
456
  msg.role === "user"
457
- ? "bg-blue-600 text-white"
458
- : "bg-gray-700 text-gray-200 prose prose-invert prose-sm max-w-none"
459
  }`}
460
  >
461
  {msg.role === "assistant" ? (
@@ -470,8 +493,8 @@ const Notes: React.FC = () => {
470
  ))}
471
  {isChatLoading && (
472
  <div className="flex justify-start">
473
- <div className="bg-gray-700 p-3 rounded-lg">
474
- <Loader2 className="animate-spin w-4 h-4 text-blue-400" />
475
  </div>
476
  </div>
477
  )}
@@ -479,7 +502,7 @@ const Notes: React.FC = () => {
479
  <div ref={messagesEndRef} />
480
  </div>
481
 
482
- <div className="p-4 border-t border-gray-700 shrink-0">
483
  <div className="flex gap-2">
484
  <input
485
  type="text"
@@ -487,13 +510,14 @@ const Notes: React.FC = () => {
487
  onChange={(e) => setInputMessage(e.target.value)}
488
  onKeyDown={(e) => e.key === "Enter" && handleSendMessage()}
489
  placeholder="Type your question..."
490
- className="flex-1 bg-gray-800 text-white rounded-lg px-4 py-2 border border-gray-700 focus:outline-none focus:border-blue-500 placeholder-gray-500"
 
491
  disabled={!sessionId || isChatLoading}
492
  />
493
  <button
494
  onClick={handleSendMessage}
495
  disabled={!sessionId || isChatLoading}
496
- className="bg-blue-600 p-2 rounded-lg text-white hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
497
  >
498
  <Send size={20} />
499
  </button>
 
13
  Trash2,
14
  Edit2,
15
  Check,
16
+ Brain,
17
  } from "lucide-react";
18
  import {
19
  fetchNotes,
 
62
  loadNotes();
63
  }, []);
64
 
65
+ // --- Robust Auto-Scroll ---
66
  const scrollToBottom = () => {
 
 
67
  messagesEndRef.current?.scrollIntoView({
68
  behavior: "instant",
69
  block: "end",
 
77
  // --- Resizing Logic ---
78
  const startResizing = useCallback(() => setIsResizing(true), []);
79
  const stopResizing = useCallback(() => setIsResizing(false), []);
80
+
81
  const resize = useCallback(
82
  (mouseMoveEvent: MouseEvent) => {
83
  if (isResizing) {
 
158
  const saveRename = async (e: React.MouseEvent) => {
159
  e.stopPropagation();
160
  if (!editingNoteId || !editName.trim()) return;
161
+
162
  try {
163
  await renameNote(editingNoteId, editName);
164
  setNotes((prev) =>
 
260
  };
261
 
262
  return (
263
+ // 1. Root Container: #434E78 Background, No Scrollbars
264
+ <div className="flex h-screen w-full overflow-hidden bg-[#434E78] font-sans text-white">
265
+ {/* --- Left Sidebar (#607B8F) --- */}
266
  <div
267
+ className={`h-screen shrink-0 transition-all duration-300 bg-[#607B8F] border-r border-white/10 flex flex-col gap-4 ${
268
+ isSidebarOpen ? "w-72 p-4" : "w-0 p-0 overflow-hidden"
269
  }`}
270
  >
271
  {isSidebarOpen && (
272
  <>
273
+ <div className="flex items-center gap-2 mb-2 border-b border-white/10 pb-4">
274
+ <FileText className="text-[#F7E396] w-6 h-6" />
275
+ <h3 className="text-xl font-bold text-white font-handwriting">
276
+ My Notes
277
+ </h3>
278
+ </div>
279
+
280
  <input
281
  type="file"
282
  ref={fileInputRef}
 
284
  accept="application/pdf"
285
  onChange={handleFileChange}
286
  />
287
+
288
+ {/* Upload Button: #F7E396 (Highlight) */}
289
  <button
290
  onClick={handleUploadClick}
291
  disabled={isUploading}
292
+ className="flex items-center justify-center gap-2 w-full bg-[#F7E396] text-[#434E78] px-4 py-3 rounded-xl hover:bg-[#E97F4A] hover:text-white transition font-bold shadow-md"
293
  >
294
  {isUploading ? (
295
  <Loader2 className="animate-spin" size={20} />
 
299
  {isUploading ? "Uploading..." : "Upload New PDF"}
300
  </button>
301
 
302
+ <div className="mt-2 flex-1 overflow-y-auto space-y-2 pr-1 custom-scrollbar">
303
+ <p className="text-xs text-gray-200 uppercase tracking-widest font-semibold mb-2">
304
  History
305
  </p>
306
 
 
308
  <div
309
  key={note.id}
310
  onClick={() => handleNoteSelect(note)}
311
+ className={`group relative p-3 rounded-lg cursor-pointer flex items-center justify-between transition-all border border-transparent ${
312
  currentNote?.id === note.id
313
+ ? "bg-[#434E78] border-[#F7E396] shadow-md"
314
+ : "hover:bg-[#434E78]/50 text-gray-100"
315
  }`}
316
  >
317
  {editingNoteId === note.id ? (
 
321
  value={editName}
322
  onChange={(e) => setEditName(e.target.value)}
323
  onClick={(e) => e.stopPropagation()}
324
+ className="flex-1 bg-[#434E78] text-white text-xs px-2 py-1 rounded border border-[#F7E396] outline-none"
325
  autoFocus
326
  />
327
  <button
328
  onClick={saveRename}
329
+ className="text-green-300 hover:text-green-200 p-1"
330
  >
331
  <Check size={14} />
332
  </button>
333
  <button
334
  onClick={cancelRename}
335
+ className="text-red-300 hover:text-red-200 p-1"
336
  >
337
  <X size={14} />
338
  </button>
339
  </div>
340
  ) : (
341
  <>
342
+ <div className="flex items-center gap-3 overflow-hidden">
343
  <FileText
344
  size={16}
345
+ className={`${
346
+ currentNote?.id === note.id
347
+ ? "text-[#F7E396]"
348
+ : "text-gray-300"
349
+ } shrink-0`}
350
  />
351
+ <span
352
+ className={`truncate text-sm font-medium ${
353
+ currentNote?.id === note.id
354
+ ? "text-white"
355
+ : "text-gray-200"
356
+ }`}
357
+ >
358
  {note.filename}
359
  </span>
360
  </div>
361
  <div className="flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity">
362
  <button
363
  onClick={(e) => startEditing(e, note)}
364
+ className="p-1.5 hover:bg-white/10 rounded text-gray-300 hover:text-[#F7E396]"
365
  title="Rename"
366
  >
367
  <Edit2 size={14} />
368
  </button>
369
  <button
370
  onClick={(e) => handleDeleteNote(e, note.id)}
371
+ className="p-1.5 hover:bg-red-500/20 rounded text-gray-300 hover:text-red-300"
372
  title="Delete"
373
  >
374
  <Trash2 size={14} />
 
383
  )}
384
  </div>
385
 
386
+ {/* --- Center: PDF Viewer (#434E78 Background) --- */}
387
+ <div className="flex flex-1 overflow-hidden relative flex-col bg-[#434E78]">
388
+ <header className="flex justify-between items-center p-4 border-b border-white/10 shrink-0 z-10 bg-[#434E78]">
389
  <div className="flex items-center gap-3">
390
  <button
391
  onClick={() => setIsSidebarOpen(!isSidebarOpen)}
392
+ className="p-2 rounded-lg hover:bg-white/10 text-white transition"
393
  >
394
  <Menu size={20} />
395
  </button>
396
+ <h2 className="text-lg font-bold text-white truncate max-w-md">
397
  {currentNote ? currentNote.filename : "Select a Note"}
398
  </h2>
399
  </div>
400
  {!isChatOpen && currentNote && (
401
  <button
402
  onClick={() => setIsChatOpen(true)}
403
+ className="flex items-center gap-2 bg-[#F7E396] text-[#434E78] hover:bg-[#E97F4A] hover:text-white px-4 py-2 rounded-lg text-sm font-bold transition shadow-md"
404
  >
405
  <MessageSquare size={18} /> Open Chat
406
  </button>
407
  )}
408
  </header>
409
 
410
+ <div className="flex-1 w-full h-full relative p-4">
411
  {pdfUrl ? (
412
+ <div className="w-full h-full rounded-xl overflow-hidden shadow-2xl border border-white/10 bg-white/5 backdrop-blur-sm">
413
+ <iframe
414
+ src={pdfUrl}
415
+ className={`w-full h-full border-none ${
416
+ isResizing ? "pointer-events-none" : ""
417
+ }`}
418
+ title="PDF Viewer"
419
+ />
420
+ </div>
421
  ) : (
422
+ <div className="flex flex-col items-center justify-center h-full text-gray-400/50">
423
+ <FileText size={80} className="mb-4 opacity-30" />
424
+ <p className="text-lg font-medium">
425
+ Select a PDF from the sidebar to view
426
+ </p>
427
  </div>
428
  )}
429
  </div>
430
  </div>
431
 
432
+ {/* --- Resizable Chat Panel (#607B8F) --- */}
433
  {isChatOpen && (
434
  <div
435
+ className="w-1 hover:w-2 cursor-col-resize bg-[#434E78] hover:bg-[#F7E396] transition-all z-20 flex items-center justify-center shrink-0"
436
  onMouseDown={startResizing}
437
  >
438
+ <GripVertical size={16} className="text-white/30" />
439
  </div>
440
  )}
441
 
442
  <div
443
  style={{ width: isChatOpen ? chatWidth : 0 }}
444
+ className={`flex flex-col bg-[#607B8F] border-l border-white/10 shrink-0 transition-all duration-100 ease-linear overflow-hidden`}
445
  >
446
  {isChatOpen && (
447
  <>
448
+ <header className="flex justify-between items-center p-4 border-b border-white/10 bg-[#607B8F] shrink-0">
449
  <div className="flex items-center gap-2">
450
+ <Brain size={20} className="text-[#F7E396]" />
451
+ <h3 className="text-lg font-bold text-white whitespace-nowrap font-handwriting">
452
  AI Chat
453
  </h3>
454
  </div>
455
  <button
456
  onClick={() => setIsChatOpen(false)}
457
+ className="p-2 rounded-lg text-gray-200 hover:bg-white/10 hover:text-white transition-colors"
458
  >
459
  <X size={20} />
460
  </button>
461
  </header>
462
 
463
+ <div className="flex-1 overflow-y-auto p-4 space-y-4 custom-scrollbar">
464
  {messages.length === 0 && (
465
+ <div className="text-center text-gray-200 mt-10 opacity-70">
466
  <p>Ask a question about this document...</p>
467
  </div>
468
  )}
469
+
470
  {messages.map((msg, i) => (
471
  <div
472
  key={i}
 
475
  }`}
476
  >
477
  <div
478
+ className={`max-w-[85%] p-4 rounded-xl text-sm shadow-sm ${
479
  msg.role === "user"
480
+ ? "bg-[#F7E396] text-[#434E78] font-medium rounded-tr-none"
481
+ : "bg-[#434E78] text-white border border-white/10 rounded-tl-none prose prose-invert prose-sm max-w-none"
482
  }`}
483
  >
484
  {msg.role === "assistant" ? (
 
493
  ))}
494
  {isChatLoading && (
495
  <div className="flex justify-start">
496
+ <div className="bg-[#434E78] p-3 rounded-lg border border-white/10">
497
+ <Loader2 className="animate-spin w-4 h-4 text-[#F7E396]" />
498
  </div>
499
  </div>
500
  )}
 
502
  <div ref={messagesEndRef} />
503
  </div>
504
 
505
+ <div className="p-4 border-t border-white/10 shrink-0 bg-[#607B8F]">
506
  <div className="flex gap-2">
507
  <input
508
  type="text"
 
510
  onChange={(e) => setInputMessage(e.target.value)}
511
  onKeyDown={(e) => e.key === "Enter" && handleSendMessage()}
512
  placeholder="Type your question..."
513
+ // Input: Dark Blue (#434E78) for contrast on Light Blue (#607B8F) panel
514
+ className="flex-1 bg-[#434E78] text-white rounded-xl px-4 py-3 border border-transparent focus:border-[#F7E396] focus:ring-0 outline-none placeholder-gray-400 shadow-inner"
515
  disabled={!sessionId || isChatLoading}
516
  />
517
  <button
518
  onClick={handleSendMessage}
519
  disabled={!sessionId || isChatLoading}
520
+ className="bg-[#F7E396] p-3 rounded-xl text-[#434E78] hover:bg-[#E97F4A] hover:text-white disabled:opacity-50 disabled:cursor-not-allowed transition-colors shadow-md"
521
  >
522
  <Send size={20} />
523
  </button>