Ashok75 commited on
Commit
72cdcd8
·
verified ·
1 Parent(s): e930453

Upload 3 files

Browse files
Files changed (3) hide show
  1. auth.html +335 -56
  2. chat.html +139 -100
  3. profile.html +2090 -0
auth.html CHANGED
@@ -40,7 +40,7 @@
40
  width: 100%;
41
  background: -webkit-linear-gradient(left, #003366,#004080,#0059b3 , #0073e6);
42
  color: var(--bs-body-color);
43
- padding-top: 5vh;
44
  padding-bottom: 5vh;
45
  min-height: 100vh;
46
  /* Allow space for modal overlay */
@@ -442,16 +442,25 @@
442
  font-size: 0.9rem;
443
  color: var(--bs-body-color);
444
  }
445
- .modal-form input[type="text"],
446
- .modal-form input[type="email"],
447
- .modal-form input[type="password"] {
448
- width: 100%;
449
- padding: 10px 15px;
 
 
 
 
 
450
  border: 1px solid var(--bs-border-color);
451
- border-radius: 10px;
 
452
  background-color: var(--bs-body-bg);
453
  color: var(--bs-body-color);
454
- box-sizing: border-box; /* Crucial for consistent width */
 
 
 
455
  }
456
  .modal-form .error-message {
457
  position: static; /* Remove absolute positioning */
@@ -469,9 +478,96 @@
469
  width: 100%;
470
  max-width: 150px;
471
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
472
  </style>
473
  </head>
474
  <body>
 
 
 
 
 
 
 
 
 
 
 
 
475
 
476
  <div class="wrapper">
477
  <div class="title-text">
@@ -540,32 +636,45 @@
540
  </div>
541
 
542
  <div class="modal-overlay" id="forgotPasswordRequestModal">
543
- <div class="modal-content">
544
- <p class="modal-message" id="forgotModalMessage">Enter your registered email to request a password reset.</p>
 
545
 
546
  <form id="forgotPasswordRequestForm" class="modal-form">
547
- <div class="field">
548
- <label for="forgotEmail">Email:</label>
549
- <input type="email" id="forgotEmail" name="email" required>
550
- <div class="error-message" id="forgotEmailError"></div>
551
  </div>
552
  <div class="modal-buttons-stacked">
553
- <button type="submit" class="modal-button" id="forgotSubmitButton">Request OTP</button>
554
- <button type="button" class="modal-button" id="forgotCancelButton" style="background-color: var(--bs-secondary-color);">Cancel</button>
555
  </div>
556
  </form>
557
 
558
  <div id="otpSection" style="display: none;">
 
 
559
  <form id="verifyOtpForm" class="modal-form">
560
- <div class="field">
561
- <label for="otpInput">Enter OTP:</label>
562
- <input type="text" id="otpInput" name="otp" required maxlength="6" pattern="\d{6}" title="Please enter a 6-digit OTP">
563
- <div class="error-message" id="otpError"></div>
 
 
 
 
 
 
 
 
 
 
564
  </div>
565
  <div class="modal-buttons-stacked">
566
- <button type="submit" class="modal-button" id="verifyOtpButton">Verify OTP</button>
567
- <button type="button" class="modal-button" id="resendOtpButton" style="background-color: var(--bs-secondary-color);">Send Again</button>
568
- <button type="button" class="modal-button" id="otpCancelButton" style="background-color: var(--bs-secondary-color);">Cancel</button>
569
  </div>
570
  </form>
571
  </div>
@@ -603,6 +712,34 @@
603
 
604
  <script>
605
  document.addEventListener('DOMContentLoaded', function() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
606
  const loginText = document.querySelector(".title-text .login");
607
  const loginFormDiv = document.querySelector("form.login");
608
  const signupLink = document.querySelector("form .signup-link a");
@@ -638,7 +775,20 @@
638
  const modalMessageDiv = document.getElementById('modalMessage');
639
  const modalButtonsContainer = document.getElementById('modalButtonsContainer');
640
 
641
- // Forgot Password Request & OTP Modals elements
 
 
 
 
 
 
 
 
 
 
 
 
 
642
  const forgotPasswordLink = document.getElementById('forgotPasswordLink');
643
  const forgotPasswordRequestModal = document.getElementById('forgotPasswordRequestModal'); // The main overlay for forgot/otp
644
  const forgotModalMessage = document.getElementById('forgotModalMessage'); // Message within the forgot/otp modal
@@ -678,6 +828,31 @@
678
  // Stores the email across the forgot password/OTP/reset flow
679
  let currentForgotEmail = '';
680
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
681
  // Function to clear all error messages and input error classes
682
  function clearErrors(formType = 'all') {
683
  if (formType === 'all' || formType === 'main') {
@@ -851,7 +1026,7 @@
851
 
852
  // Login Form Submission Handler
853
  if (loginForm) {
854
- loginForm.addEventListener('submit', function(event) {
855
  event.preventDefault();
856
  clearErrors('main'); // Clear main form errors
857
 
@@ -878,6 +1053,23 @@
878
  }
879
 
880
  if (isValid) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
881
  submitFormData(event.target, loginSubmitButton, '/api/login', 'main');
882
  }
883
  });
@@ -885,7 +1077,7 @@
885
 
886
  // Signup Form Submission Handler
887
  if (signupForm) {
888
- signupForm.addEventListener('submit', function(event) {
889
  event.preventDefault();
890
  clearErrors('main');
891
 
@@ -934,6 +1126,23 @@
934
  }
935
 
936
  if (isValid) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
937
  submitFormData(event.target, signupSubmitButton, '/api/signup', 'main');
938
  }
939
  });
@@ -955,6 +1164,12 @@
955
  forgotPasswordRequestForm.style.display = 'block';
956
  if (otpSection) otpSection.style.display = 'none'; // Ensure OTP section is hidden initially
957
 
 
 
 
 
 
 
958
  showCustomModal(forgotPasswordRequestModal);
959
  });
960
  }
@@ -999,6 +1214,12 @@
999
  if (otpSection) otpSection.style.display = 'block';
1000
  otpInput.value = ''; // Clear previous OTP in case of resend
1001
 
 
 
 
 
 
 
1002
  // Update message for OTP section
1003
  let messageText = `<p>An OTP has been sent to <strong>${email}</strong>. Please enter it below.</p>`;
1004
  // Note: Do not display simulated OTP in production. This is for testing convenience.
@@ -1006,9 +1227,30 @@
1006
  // messageText += `<p><strong>SIMULATED OTP (FOR TESTING):</strong> ${result.simulated_otp}</p>`;
1007
  // }
1008
  forgotModalMessage.innerHTML = messageText;
 
1009
 
1010
  } else {
1011
- displayModalFormErrors(result, forgotPasswordRequestForm, 'forgot');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1012
  }
1013
  } catch (error) {
1014
  console.error('Fetch error:', error);
@@ -1028,19 +1270,17 @@
1028
  clearErrors('otp'); // Clear errors specific to the OTP verification form
1029
 
1030
  let isValid = true;
1031
- const otp = otpInput.value.trim();
 
1032
 
1033
- if (otp === '') {
1034
  isValid = false;
1035
- otpError.textContent = 'OTP is required.';
1036
- otpInput.classList.add('error');
1037
- } else if (!/^\d{6}$/.test(otp)) { // Ensure it's 6 digits
1038
- isValid = false;
1039
- otpError.textContent = 'OTP must be a 6-digit number.';
1040
- otpInput.classList.add('error');
1041
  }
1042
 
1043
  if (isValid) {
 
1044
  verifyOtpButton.disabled = true;
1045
  verifyOtpButton.innerHTML = '<span class="loading-spinner"></span> Verifying...';
1046
 
@@ -1106,6 +1346,7 @@
1106
  otpInput.value = ''; // Clear previous OTP
1107
  otpInput.classList.remove('error'); // Clear error state
1108
  otpError.textContent = ''; // Clear error message
 
1109
  } else {
1110
  // If there's an error on resend, display it in the OTP section's error message
1111
  otpError.textContent = result.message || 'Failed to resend OTP.';
@@ -1131,6 +1372,16 @@
1131
  forgotPasswordRequestForm.reset();
1132
  currentForgotEmail = ''; // Clear stored email
1133
  clearErrors('forgot'); // Ensure all forgot-related errors are cleared
 
 
 
 
 
 
 
 
 
 
1134
  });
1135
  }
1136
 
@@ -1140,6 +1391,16 @@
1140
  verifyOtpForm.reset();
1141
  currentForgotEmail = ''; // Clear stored email
1142
  clearErrors('otp'); // Ensure all OTP-related errors are cleared
 
 
 
 
 
 
 
 
 
 
1143
  });
1144
  }
1145
 
@@ -1149,6 +1410,16 @@
1149
  resetPasswordForm.reset();
1150
  currentForgotEmail = ''; // Clear stored email
1151
  clearErrors('reset'); // Ensure all reset-related errors are cleared
 
 
 
 
 
 
 
 
 
 
1152
  });
1153
  }
1154
 
@@ -1213,6 +1484,16 @@
1213
  hideCustomModal(resetPasswordModal);
1214
  resetPasswordForm.reset();
1215
  currentForgotEmail = ''; // Clear stored email after successful reset
 
 
 
 
 
 
 
 
 
 
1216
  showFlexibleModal(
1217
  '<p>Password reset successful. You can now login with your new password.</p>',
1218
  [{ text: 'OK', action: () => {
@@ -1222,6 +1503,7 @@
1222
  );
1223
  } else {
1224
  // Display error messages from the backend
 
1225
  displayModalFormErrors(result, resetPasswordForm, 'reset');
1226
  if (result.message && (result.message.includes('OTP not verified') || result.message.includes('not initiated'))) {
1227
  showFlexibleModal(
@@ -1313,7 +1595,10 @@
1313
  // Update the main message area of the modal if a general message is returned
1314
  const targetModalMessage = formElement.closest('.modal-content').querySelector('.modal-message');
1315
  if (targetModalMessage && result.message) {
1316
- targetModalMessage.innerHTML = `<p>${result.message}</p>`;
 
 
 
1317
  }
1318
  }
1319
 
@@ -1368,24 +1653,15 @@
1368
  }}]
1369
  );
1370
  } else if (endpoint === '/api/login') {
1371
- showFlexibleModal(
1372
- '<p>Login successful!</p>',
1373
- [{
1374
- text: 'OK',
1375
- action: () => {
1376
- // Mark user as logged in so chat.html knows
1377
- localStorage.setItem('gakr_is_logged_in', 'true');
1378
- alert('Welcome! You are logged in.');
1379
- // Optional: remove any guest flag if you had one
1380
- // localStorage.removeItem('gakr_is_guest');
1381
-
1382
- // Redirect back to chat interface
1383
- window.location.href = 'chat.html';
1384
- }
1385
- }]
1386
- );
1387
- }
1388
-
1389
  }
1390
  // For forgot/otp/reset, successful handling is now done directly in their respective event listeners
1391
  // so no generic handling here for those contexts.
@@ -1407,10 +1683,13 @@
1407
  handleMainFormErrors(result, formElement, endpoint);
1408
  }
1409
  } else if (formContext === 'forgot') {
 
1410
  displayModalFormErrors(result, formElement, 'forgot');
1411
  } else if (formContext === 'otp') {
 
1412
  displayModalFormErrors(result, formElement, 'otp');
1413
  } else if (formContext === 'reset') {
 
1414
  displayModalFormErrors(result, formElement, 'reset');
1415
  }
1416
  }
@@ -1428,9 +1707,9 @@
1428
  submitButton.disabled = false;
1429
  // Reset button text specifically for modal forms if not handled by success/error flows above
1430
  if (formContext === 'forgot') {
1431
- submitButton.innerHTML = 'Request OTP';
1432
  } else if (formContext === 'otp') {
1433
- submitButton.innerHTML = 'Verify OTP';
1434
  } else if (formContext === 'reset') {
1435
  submitButton.innerHTML = 'Reset Password';
1436
  }
 
40
  width: 100%;
41
  background: -webkit-linear-gradient(left, #003366,#004080,#0059b3 , #0073e6);
42
  color: var(--bs-body-color);
43
+ padding-top: calc(64px + 5vh);
44
  padding-bottom: 5vh;
45
  min-height: 100vh;
46
  /* Allow space for modal overlay */
 
442
  font-size: 0.9rem;
443
  color: var(--bs-body-color);
444
  }
445
+ .otp-input-container {
446
+ display: flex;
447
+ gap: 5px;
448
+ justify-content: center;
449
+ margin-bottom: 10px;
450
+ }
451
+ .otp-digit {
452
+ width: 40px;
453
+ height: 40px;
454
+ text-align: center;
455
  border: 1px solid var(--bs-border-color);
456
+ border-radius: 5px;
457
+ font-size: 1.2rem;
458
  background-color: var(--bs-body-bg);
459
  color: var(--bs-body-color);
460
+ }
461
+ .otp-digit:focus {
462
+ border-color: var(--gakr-blue);
463
+ outline: none;
464
  }
465
  .modal-form .error-message {
466
  position: static; /* Remove absolute positioning */
 
478
  width: 100%;
479
  max-width: 150px;
480
  }
481
+
482
+ /* Header styles */
483
+ .gakr-chat-header {
484
+ padding: 0.75rem 1.5rem;
485
+ display: flex;
486
+ justify-content: space-between;
487
+ align-items: center;
488
+ border-bottom: 1px solid var(--bs-border-color);
489
+ height: 64px;
490
+ position: fixed;
491
+ top: 0;
492
+ left: 0;
493
+ right: 0;
494
+ width: 100%;
495
+ background-color: var(--bs-body-bg);
496
+ z-index: 10;
497
+ }
498
+
499
+ .gakr-logo-area {
500
+ display: flex;
501
+ align-items: center;
502
+ gap: 0.75rem;
503
+ }
504
+
505
+ .gakr-brand-logo {
506
+ width: 24px;
507
+ height: 24px;
508
+ display: flex;
509
+ align-items: center;
510
+ justify-content: center;
511
+ }
512
+
513
+ .gakr-brand-text {
514
+ font-size: 1.25rem;
515
+ font-weight: 500;
516
+ color: var(--gakr-blue);
517
+ }
518
+
519
+ .gakr-nav-controls {
520
+ display: flex;
521
+ align-items: center;
522
+ }
523
+
524
+ .gakr-login-button {
525
+ color: var(--gakr-blue);
526
+ text-decoration: none;
527
+ background: transparent;
528
+ border: 1px solid var(--gakr-blue);
529
+ padding: 0.5rem 1rem;
530
+ border-radius: 50px;
531
+ font-size: 0.9rem;
532
+ transition: background-color 0.2s;
533
+ }
534
+
535
+ .gakr-login-button:hover {
536
+ background-color: rgba(66, 133, 244, 0.1);
537
+ }
538
+
539
+ .gakr-profile-button {
540
+ color: var(--gakr-blue);
541
+ text-decoration: none;
542
+ background: transparent;
543
+ border: none;
544
+ padding: 0.5rem 1rem;
545
+ border-radius: 50px;
546
+ font-size: 0.9rem;
547
+ transition: background-color 0.2s;
548
+ display: flex;
549
+ align-items: center;
550
+ gap: 0.5rem;
551
+ }
552
+
553
+ .gakr-profile-button:hover {
554
+ background-color: rgba(66, 133, 244, 0.1);
555
+ }
556
  </style>
557
  </head>
558
  <body>
559
+ <header class="gakr-chat-header">
560
+ <div class="gakr-logo-area">
561
+ <a href="/" class="gakr-brand-logo">
562
+ <i class="fas fa-robot" style="color: var(--gakr-blue);"></i>
563
+ </a>
564
+ <a href="/" class="gakr-brand-text" style="text-decoration: none;">GAKR AI</a>
565
+ </div>
566
+
567
+ <div class="gakr-nav-controls" id="authButtonContainer">
568
+ <a href="/auth" class="gakr-login-button">Sign in / Register</a>
569
+ </div>
570
+ </header>
571
 
572
  <div class="wrapper">
573
  <div class="title-text">
 
636
  </div>
637
 
638
  <div class="modal-overlay" id="forgotPasswordRequestModal">
639
+ <div class="modal-content" style="max-width: 450px;">
640
+ <h3 style="text-align: center; margin-bottom: 20px; color: var(--gakr-blue);">Reset Password</h3>
641
+ <p class="modal-message" id="forgotModalMessage" style="text-align: center; margin-bottom: 25px;">Enter your registered email address to receive a password reset code.</p>
642
 
643
  <form id="forgotPasswordRequestForm" class="modal-form">
644
+ <div class="field" style="margin-bottom: 30px;">
645
+ <label for="forgotEmail" style="display: block; margin-bottom: 8px; font-weight: 500; color: var(--bs-body-color);">Email Address:</label>
646
+ <input type="email" id="forgotEmail" name="email" required style="width: 100%; padding: 12px; border: 2px solid var(--bs-border-color); border-radius: 8px; font-size: 16px; background-color: var(--bs-body-bg); color: var(--bs-body-color);" placeholder="Enter your email">
647
+ <div class="error-message" id="forgotEmailError" style="margin-top: 8px;"></div>
648
  </div>
649
  <div class="modal-buttons-stacked">
650
+ <button type="submit" class="modal-button" id="forgotSubmitButton" style="width: 100%; padding: 12px; font-size: 16px;">Send Reset Code</button>
651
+ <button type="button" class="modal-button" id="forgotCancelButton" style="width: 100%; padding: 12px; font-size: 16px; background-color: var(--bs-secondary-color); margin-top: 10px;">Cancel</button>
652
  </div>
653
  </form>
654
 
655
  <div id="otpSection" style="display: none;">
656
+ <h3 style="text-align: center; margin-bottom: 20px; color: var(--gakr-blue);">Enter Verification Code</h3>
657
+ <p style="text-align: center; margin-bottom: 25px; color: var(--bs-secondary-color);">We've sent a 8-digit code to your email. Enter it below to continue.</p>
658
  <form id="verifyOtpForm" class="modal-form">
659
+ <div class="field" style="margin-bottom: 30px;">
660
+ <label style="display: block; margin-bottom: 15px; font-weight: 500; color: var(--bs-body-color); text-align: center;">Verification Code:</label>
661
+ <div class="otp-input-container" style="display: flex; justify-content: center; gap: 8px; margin-bottom: 10px;">
662
+ <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">
663
+ <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">
664
+ <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">
665
+ <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">
666
+ <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">
667
+ <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">
668
+ <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">
669
+ <input type="text" class="otp-digit" maxlength="1" pattern="\d" required style="width: 45px; height: 50px; text-align: center; font-size: 20px; font-weight: bold; border: 2px solid var(--bs-border-color); border-radius: 8px; background-color: var(--bs-body-bg); color: var(--bs-body-color);">
670
+ </div>
671
+ <input type="hidden" id="otpInput" name="otp">
672
+ <div class="error-message" id="otpError" style="text-align: center; margin-top: 10px;"></div>
673
  </div>
674
  <div class="modal-buttons-stacked">
675
+ <button type="submit" class="modal-button" id="verifyOtpButton" style="width: 100%; padding: 12px; font-size: 16px;">Verify Code</button>
676
+ <button type="button" class="modal-button" id="resendOtpButton" style="width: 100%; padding: 12px; font-size: 16px; margin-top: 10px;">Send Code Again</button>
677
+ <button type="button" class="modal-button" id="otpCancelButton" style="width: 100%; padding: 12px; font-size: 16px; background-color: var(--bs-secondary-color); margin-top: 10px;">Cancel</button>
678
  </div>
679
  </form>
680
  </div>
 
712
 
713
  <script>
714
  document.addEventListener('DOMContentLoaded', function() {
715
+ // --- Authentication Status Check ---
716
+ function updateAuthButton() {
717
+ const authButtonContainer = document.getElementById('authButtonContainer');
718
+ const userIsLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
719
+
720
+ if (userIsLoggedIn) {
721
+ authButtonContainer.innerHTML = `
722
+ <a href="/profile" class="gakr-profile-button">
723
+ <i class="fas fa-user-circle me-2"></i>Profile
724
+ </a>
725
+ `;
726
+ const profileButton = authButtonContainer.querySelector('.gakr-profile-button');
727
+ if (profileButton) {
728
+ profileButton.addEventListener('click', function(event) {
729
+ console.log("Profile button clicked!");
730
+ });
731
+ }
732
+ } else {
733
+ authButtonContainer.innerHTML = `
734
+ <a href="/auth" class="gakr-login-button">Sign in / Register</a>
735
+ `;
736
+ }
737
+ }
738
+
739
+ updateAuthButton();
740
+ window.addEventListener('focus', updateAuthButton);
741
+ window.addEventListener('storage', updateAuthButton);
742
+
743
  const loginText = document.querySelector(".title-text .login");
744
  const loginFormDiv = document.querySelector("form.login");
745
  const signupLink = document.querySelector("form .signup-link a");
 
775
  const modalMessageDiv = document.getElementById('modalMessage');
776
  const modalButtonsContainer = document.getElementById('modalButtonsContainer');
777
 
778
+ // OTP digit inputs
779
+ const otpDigits = document.querySelectorAll('.otp-digit');
780
+ otpDigits.forEach((input, index) => {
781
+ input.addEventListener('input', function() {
782
+ if (this.value.length === 1 && index < otpDigits.length - 1) {
783
+ otpDigits[index + 1].focus();
784
+ }
785
+ });
786
+ input.addEventListener('keydown', function(e) {
787
+ if (e.key === 'Backspace' && this.value === '' && index > 0) {
788
+ otpDigits[index - 1].focus();
789
+ }
790
+ });
791
+ });
792
  const forgotPasswordLink = document.getElementById('forgotPasswordLink');
793
  const forgotPasswordRequestModal = document.getElementById('forgotPasswordRequestModal'); // The main overlay for forgot/otp
794
  const forgotModalMessage = document.getElementById('forgotModalMessage'); // Message within the forgot/otp modal
 
828
  // Stores the email across the forgot password/OTP/reset flow
829
  let currentForgotEmail = '';
830
 
831
+ // Resend OTP cooldown variables
832
+ let resendCooldown = 0;
833
+ let resendInterval = null;
834
+
835
+ // Function to start resend cooldown timer
836
+ function startResendCooldown() {
837
+ resendCooldown = 60; // 60 seconds
838
+ resendOtpButton.disabled = true;
839
+ resendOtpButton.textContent = `Send Again (60s)`;
840
+ resendOtpButton.style.backgroundColor = 'var(--bs-secondary-color)'; // Gray during cooldown
841
+
842
+ resendInterval = setInterval(() => {
843
+ resendCooldown--;
844
+ if (resendCooldown > 0) {
845
+ resendOtpButton.textContent = `Send Again (${resendCooldown}s)`;
846
+ } else {
847
+ clearInterval(resendInterval);
848
+ resendInterval = null;
849
+ resendOtpButton.disabled = false;
850
+ resendOtpButton.textContent = 'Send Code Again';
851
+ resendOtpButton.style.backgroundColor = ''; // Reset to default (primary blue)
852
+ }
853
+ }, 1000);
854
+ }
855
+
856
  // Function to clear all error messages and input error classes
857
  function clearErrors(formType = 'all') {
858
  if (formType === 'all' || formType === 'main') {
 
1026
 
1027
  // Login Form Submission Handler
1028
  if (loginForm) {
1029
+ loginForm.addEventListener('submit', async function(event) {
1030
  event.preventDefault();
1031
  clearErrors('main'); // Clear main form errors
1032
 
 
1053
  }
1054
 
1055
  if (isValid) {
1056
+ // Check if user exists
1057
+ try {
1058
+ const checkResponse = await fetch('/api/check_user', {
1059
+ method: 'POST',
1060
+ headers: { 'Content-Type': 'application/json' },
1061
+ body: JSON.stringify({ identifier: usernameOrEmail })
1062
+ });
1063
+ const checkData = await checkResponse.json();
1064
+ if (!checkData.exists) {
1065
+ loginUsernameOrEmailError.textContent = 'User does not exist.';
1066
+ loginUsernameOrEmailInput.classList.add('error');
1067
+ return;
1068
+ }
1069
+ } catch (error) {
1070
+ console.error('Error checking user:', error);
1071
+ // Proceed anyway
1072
+ }
1073
  submitFormData(event.target, loginSubmitButton, '/api/login', 'main');
1074
  }
1075
  });
 
1077
 
1078
  // Signup Form Submission Handler
1079
  if (signupForm) {
1080
+ signupForm.addEventListener('submit', async function(event) {
1081
  event.preventDefault();
1082
  clearErrors('main');
1083
 
 
1126
  }
1127
 
1128
  if (isValid) {
1129
+ // Check if user already exists
1130
+ try {
1131
+ const checkResponse = await fetch('/api/check_user', {
1132
+ method: 'POST',
1133
+ headers: { 'Content-Type': 'application/json' },
1134
+ body: JSON.stringify({ identifier: email })
1135
+ });
1136
+ const checkData = await checkResponse.json();
1137
+ if (checkData.exists) {
1138
+ signupEmailError.textContent = 'Email already registered.';
1139
+ signupEmailInput.classList.add('error');
1140
+ return;
1141
+ }
1142
+ } catch (error) {
1143
+ console.error('Error checking user:', error);
1144
+ // Proceed anyway
1145
+ }
1146
  submitFormData(event.target, signupSubmitButton, '/api/signup', 'main');
1147
  }
1148
  });
 
1164
  forgotPasswordRequestForm.style.display = 'block';
1165
  if (otpSection) otpSection.style.display = 'none'; // Ensure OTP section is hidden initially
1166
 
1167
+ // Reset modal size for email input
1168
+ const modalContent = forgotPasswordRequestModal.querySelector('.modal-content');
1169
+ if (modalContent) {
1170
+ modalContent.style.maxWidth = '450px';
1171
+ }
1172
+
1173
  showCustomModal(forgotPasswordRequestModal);
1174
  });
1175
  }
 
1214
  if (otpSection) otpSection.style.display = 'block';
1215
  otpInput.value = ''; // Clear previous OTP in case of resend
1216
 
1217
+ // Increase modal size for OTP section
1218
+ const modalContent = forgotPasswordRequestModal.querySelector('.modal-content');
1219
+ if (modalContent) {
1220
+ modalContent.style.maxWidth = '550px';
1221
+ }
1222
+
1223
  // Update message for OTP section
1224
  let messageText = `<p>An OTP has been sent to <strong>${email}</strong>. Please enter it below.</p>`;
1225
  // Note: Do not display simulated OTP in production. This is for testing convenience.
 
1227
  // messageText += `<p><strong>SIMULATED OTP (FOR TESTING):</strong> ${result.simulated_otp}</p>`;
1228
  // }
1229
  forgotModalMessage.innerHTML = messageText;
1230
+ startResendCooldown(); // Start the cooldown timer for the resend button
1231
 
1232
  } else {
1233
+ // Handle different error types
1234
+ if (response.status === 404) {
1235
+ // Email not registered - show registration modal
1236
+ hideCustomModal(forgotPasswordRequestModal);
1237
+ showFlexibleModal(
1238
+ '<p>The email address you entered is not registered yet.</p><p>Please sign up to create an account.</p>',
1239
+ [
1240
+ { text: 'Sign Up', action: () => {
1241
+ signupRadio.checked = true;
1242
+ slideToSignup();
1243
+ // Pre-fill the email in signup form
1244
+ signupEmailInput.value = email;
1245
+ }},
1246
+ { text: 'Try Again', style: 'background-color: var(--bs-secondary-color);' }
1247
+ ]
1248
+ );
1249
+ } else {
1250
+ // Other errors (400, 500) - show in modal
1251
+ result.status = response.status;
1252
+ displayModalFormErrors(result, forgotPasswordRequestForm, 'forgot');
1253
+ }
1254
  }
1255
  } catch (error) {
1256
  console.error('Fetch error:', error);
 
1270
  clearErrors('otp'); // Clear errors specific to the OTP verification form
1271
 
1272
  let isValid = true;
1273
+ const otpDigits = document.querySelectorAll('.otp-digit');
1274
+ const otp = Array.from(otpDigits).map(d => d.value).join('');
1275
 
1276
+ if (otp.length !== 8 || !/^\d{8}$/.test(otp)) {
1277
  isValid = false;
1278
+ otpError.textContent = 'Please enter all 8 digits of the OTP.';
1279
+ otpDigits.forEach(d => d.classList.add('error'));
 
 
 
 
1280
  }
1281
 
1282
  if (isValid) {
1283
+ otpInput.value = otp; // Set hidden input
1284
  verifyOtpButton.disabled = true;
1285
  verifyOtpButton.innerHTML = '<span class="loading-spinner"></span> Verifying...';
1286
 
 
1346
  otpInput.value = ''; // Clear previous OTP
1347
  otpInput.classList.remove('error'); // Clear error state
1348
  otpError.textContent = ''; // Clear error message
1349
+ startResendCooldown(); // Start the cooldown timer
1350
  } else {
1351
  // If there's an error on resend, display it in the OTP section's error message
1352
  otpError.textContent = result.message || 'Failed to resend OTP.';
 
1372
  forgotPasswordRequestForm.reset();
1373
  currentForgotEmail = ''; // Clear stored email
1374
  clearErrors('forgot'); // Ensure all forgot-related errors are cleared
1375
+ // Clear resend cooldown
1376
+ if (resendInterval) {
1377
+ clearInterval(resendInterval);
1378
+ resendInterval = null;
1379
+ resendCooldown = 0;
1380
+ }
1381
+ // Reset resend button styling
1382
+ resendOtpButton.disabled = false;
1383
+ resendOtpButton.textContent = 'Send Code Again';
1384
+ resendOtpButton.style.backgroundColor = '';
1385
  });
1386
  }
1387
 
 
1391
  verifyOtpForm.reset();
1392
  currentForgotEmail = ''; // Clear stored email
1393
  clearErrors('otp'); // Ensure all OTP-related errors are cleared
1394
+ // Clear resend cooldown
1395
+ if (resendInterval) {
1396
+ clearInterval(resendInterval);
1397
+ resendInterval = null;
1398
+ resendCooldown = 0;
1399
+ }
1400
+ // Reset resend button styling
1401
+ resendOtpButton.disabled = false;
1402
+ resendOtpButton.textContent = 'Send Code Again';
1403
+ resendOtpButton.style.backgroundColor = '';
1404
  });
1405
  }
1406
 
 
1410
  resetPasswordForm.reset();
1411
  currentForgotEmail = ''; // Clear stored email
1412
  clearErrors('reset'); // Ensure all reset-related errors are cleared
1413
+ // Clear resend cooldown
1414
+ if (resendInterval) {
1415
+ clearInterval(resendInterval);
1416
+ resendInterval = null;
1417
+ resendCooldown = 0;
1418
+ }
1419
+ // Reset resend button styling
1420
+ resendOtpButton.disabled = false;
1421
+ resendOtpButton.textContent = 'Send Code Again';
1422
+ resendOtpButton.style.backgroundColor = '';
1423
  });
1424
  }
1425
 
 
1484
  hideCustomModal(resetPasswordModal);
1485
  resetPasswordForm.reset();
1486
  currentForgotEmail = ''; // Clear stored email after successful reset
1487
+ // Clear resend cooldown
1488
+ if (resendInterval) {
1489
+ clearInterval(resendInterval);
1490
+ resendInterval = null;
1491
+ resendCooldown = 0;
1492
+ }
1493
+ // Reset resend button styling
1494
+ resendOtpButton.disabled = false;
1495
+ resendOtpButton.textContent = 'Send Code Again';
1496
+ resendOtpButton.style.backgroundColor = '';
1497
  showFlexibleModal(
1498
  '<p>Password reset successful. You can now login with your new password.</p>',
1499
  [{ text: 'OK', action: () => {
 
1503
  );
1504
  } else {
1505
  // Display error messages from the backend
1506
+ result.status = response.status;
1507
  displayModalFormErrors(result, resetPasswordForm, 'reset');
1508
  if (result.message && (result.message.includes('OTP not verified') || result.message.includes('not initiated'))) {
1509
  showFlexibleModal(
 
1595
  // Update the main message area of the modal if a general message is returned
1596
  const targetModalMessage = formElement.closest('.modal-content').querySelector('.modal-message');
1597
  if (targetModalMessage && result.message) {
1598
+ // Check if this is an error response (status codes 400-599 indicate errors)
1599
+ const isError = result.status >= 400 && result.status < 600;
1600
+ const messageClass = isError ? 'error-message' : '';
1601
+ targetModalMessage.innerHTML = `<p class="${messageClass}">${result.message}</p>`;
1602
  }
1603
  }
1604
 
 
1653
  }}]
1654
  );
1655
  } else if (endpoint === '/api/login') {
1656
+ showFlexibleModal(
1657
+ '<p>Login successful!</p>',
1658
+ [{ text: 'OK', action: () => {
1659
+ localStorage.setItem('isLoggedIn', 'true');
1660
+ localStorage.setItem('user', JSON.stringify(result.user));
1661
+ window.location.href = '/';
1662
+ }}]
1663
+ );
1664
+ }
 
 
 
 
 
 
 
 
 
1665
  }
1666
  // For forgot/otp/reset, successful handling is now done directly in their respective event listeners
1667
  // so no generic handling here for those contexts.
 
1683
  handleMainFormErrors(result, formElement, endpoint);
1684
  }
1685
  } else if (formContext === 'forgot') {
1686
+ result.status = response.status;
1687
  displayModalFormErrors(result, formElement, 'forgot');
1688
  } else if (formContext === 'otp') {
1689
+ result.status = response.status;
1690
  displayModalFormErrors(result, formElement, 'otp');
1691
  } else if (formContext === 'reset') {
1692
+ result.status = response.status;
1693
  displayModalFormErrors(result, formElement, 'reset');
1694
  }
1695
  }
 
1707
  submitButton.disabled = false;
1708
  // Reset button text specifically for modal forms if not handled by success/error flows above
1709
  if (formContext === 'forgot') {
1710
+ submitButton.innerHTML = 'Send Reset Code';
1711
  } else if (formContext === 'otp') {
1712
+ submitButton.innerHTML = 'Verify Code';
1713
  } else if (formContext === 'reset') {
1714
  submitButton.innerHTML = 'Reset Password';
1715
  }
chat.html CHANGED
@@ -29,7 +29,7 @@
29
  }
30
 
31
  /* --- Base Chat Layout --- */
32
- .gemini-chat-layout {
33
  display: flex;
34
  flex-direction: column;
35
  height: 100vh;
@@ -38,7 +38,7 @@
38
  color: var(--bs-body-color); /* Use BS var */
39
  }
40
 
41
- .gemini-chat-header {
42
  padding: 0.75rem 1.5rem;
43
  display: flex;
44
  justify-content: space-between;
@@ -47,13 +47,13 @@
47
  height: 64px;
48
  }
49
 
50
- .gemini-logo-area {
51
  display: flex;
52
  align-items: center;
53
  gap: 0.75rem;
54
  }
55
 
56
- .gemini-brand-logo {
57
  width: 24px;
58
  height: 24px;
59
  display: flex;
@@ -61,13 +61,13 @@
61
  justify-content: center;
62
  }
63
 
64
- .gemini-brand-text {
65
  font-size: 1.25rem;
66
  font-weight: 500;
67
  color: var(--gakr-blue);
68
  }
69
 
70
- .gemini-chat-wrapper {
71
  flex: 1;
72
  overflow: hidden;
73
  position: relative;
@@ -75,7 +75,7 @@
75
  flex-direction: column;
76
  }
77
 
78
- .gemini-chat-container {
79
  flex: 1;
80
  overflow-y: auto;
81
  padding: 1rem;
@@ -87,7 +87,7 @@
87
  margin: 0 auto;
88
  }
89
 
90
- .gemini-prompt-area {
91
  padding: 1rem;
92
  border-top: 1px solid var(--bs-border-color);
93
  width: 100%;
@@ -98,22 +98,22 @@
98
  /* --- END MODIFIED CSS --- */
99
  }
100
 
101
- .gemini-message {
102
  display: flex;
103
  flex-direction: column;
104
  gap: 0.5rem;
105
  max-width: 90%;
106
  }
107
 
108
- .gemini-message-user {
109
  align-self: flex-end;
110
  }
111
 
112
- .gemini-message-ai {
113
  align-self: flex-start;
114
  }
115
 
116
- .gemini-message-header {
117
  display: flex;
118
  align-items: center;
119
  gap: 0.5rem;
@@ -121,7 +121,7 @@
121
  color: var(--bs-secondary-color);
122
  }
123
 
124
- .gemini-message-avatar {
125
  width: 24px;
126
  height: 24px;
127
  border-radius: 50%;
@@ -131,7 +131,7 @@
131
  font-size: 0.75rem;
132
  }
133
 
134
- .gemini-message-content {
135
  padding: 1rem;
136
  border-radius: 12px;
137
  line-height: 1.5;
@@ -140,27 +140,27 @@
140
  word-break: break-word;
141
  }
142
 
143
- .gemini-message-user .gemini-message-content {
144
  background-color: var(--bs-tertiary-bg);
145
  border-top-right-radius: 4px;
146
  }
147
 
148
- .gemini-message-ai .gemini-message-content {
149
  background-color: rgba(66, 133, 244, 0.1);
150
  border-top-left-radius: 4px;
151
  }
152
 
153
- .gemini-message-user .gemini-message-avatar {
154
  background-color: var(--bs-tertiary-bg);
155
  }
156
 
157
- .gemini-message-ai .gemini-message-avatar {
158
  background-color: rgba(66, 133, 244, 0.2);
159
  color: var(--gakr-blue);
160
  }
161
 
162
  /* --- MODIFIED CSS FOR INPUT/TEXTAREA AND ACTIONS --- */
163
- .gemini-input-container {
164
  position: relative; /* Keep relative */
165
  border-radius: 24px;
166
  border: 1px solid var(--bs-border-color);
@@ -178,14 +178,14 @@
178
  }
179
 
180
  /* Style when the container or its children are focused */
181
- .gemini-input-container:focus-within {
182
  border-color: var(--gakr-blue);
183
  box-shadow: 0 1px 8px rgba(66, 133, 244, 0.2);
184
  background: var(--bs-body-bg);
185
  }
186
 
187
  /* Style the textarea (#userInput) */
188
- .gemini-input { /* Targets the textarea with id="userInput" */
189
  flex: 1; /* Textarea takes available space */
190
  border: none;
191
  background: transparent;
@@ -209,12 +209,12 @@
209
  transition: height 0.3s ease-in-out; /* Animate height change */
210
  }
211
 
212
- .gemini-input:focus {
213
  outline: none;
214
  }
215
 
216
 
217
- .gemini-input-actions {
218
  display: flex;
219
  /* --- MODIFIED: align-items to flex-end for textarea --- */
220
  align-items: flex-end;
@@ -226,8 +226,8 @@
226
  flex-shrink: 0; /* Prevent shrinking */
227
  }
228
 
229
- /* Reusing .gemini-action-button styles for new '+' button */
230
- .gemini-action-button {
231
  width: 36px;
232
  height: 36px;
233
  border-radius: 50%;
@@ -240,50 +240,50 @@
240
  transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
241
  }
242
 
243
- .gemini-action-button:hover {
244
  background-color: var(--bs-tertiary-bg); /* Use BS var */
245
  color: var(--bs-body-color); /* Darken icon color slightly on hover */
246
  }
247
 
248
- .gemini-action-button:active {
249
  transform: scale(0.95); /* Slight press effect */
250
  background-color: var(--bs-tertiary-bg); /* Keep hover bg on active */
251
  color: var(--bs-body-color);
252
  transition: background-color 0s, transform 0.1s; /* Make feedback immediate */
253
  }
254
 
255
- .gemini-submit-button {
256
  color: var(--gakr-blue); /* Use CSS var */
257
  }
258
 
259
  /* Specific styles for submit button hover/active */
260
- .gemini-submit-button:hover {
261
  background-color: var(--gakr-blue-light); /* Light blue background on hover */
262
  color: var(--gakr-blue-dark); /* Darker blue icon on hover */
263
  }
264
 
265
- .gemini-submit-button:active {
266
  transform: scale(0.95); /* Slight press effect */
267
  background-color: var(--gakr-blue-light);
268
  color: var(--gakr-blue-dark);
269
  transition: background-color 0s, transform 0.1s;
270
  }
271
 
272
- .gemini-submit-button.disabled {
273
  opacity: 0.5;
274
  cursor: default;
275
  /* Ensure transitions are off when disabled to avoid weird states */
276
  transition: none;
277
  }
278
 
279
- .gemini-submit-button.disabled:hover {
280
  background-color: transparent;
281
  color: var(--gakr-blue); /* Keep original color when disabled */
282
  }
283
  /* --- END MODIFIED CSS --- */
284
 
285
 
286
- .gemini-typing {
287
  display: inline-flex;
288
  align-items: center;
289
  gap: 4px;
@@ -294,7 +294,7 @@
294
  align-self: flex-end;
295
  }
296
 
297
- .gemini-typing-dot {
298
  width: 6px;
299
  height: 6px;
300
  border-radius: 50%;
@@ -302,11 +302,11 @@
302
  animation: typing 1.3s infinite ease-in-out;
303
  }
304
 
305
- .gemini-typing-dot:nth-child(1) { animation-delay: 0s; }
306
- .gemini-typing-dot:nth-child(2) { animation-delay: 0.2s; }
307
- .gemini-typing-dot:nth-child(3) { animation-delay: 0.4s; }
308
 
309
- .gemini-thinking-toggle {
310
  background: none;
311
  border: none;
312
  color: var(--gakr-blue);
@@ -317,7 +317,7 @@
317
  text-decoration: underline;
318
  }
319
 
320
- .gemini-thinking-content {
321
  display: block;
322
  margin-top: 0.5rem;
323
  padding: 0.5rem;
@@ -326,26 +326,26 @@
326
  font-style: italic;
327
  }
328
 
329
- .gemini-welcome {
330
  text-align: center;
331
  max-width: 600px;
332
  margin: 4rem auto;
333
  }
334
 
335
- .gemini-welcome-icon {
336
  font-size: 3rem;
337
  margin-bottom: 1.5rem;
338
  color: var(--gakr-blue);
339
  }
340
 
341
- .gemini-welcome-title {
342
  font-size: 2rem;
343
  margin-bottom: 1rem;
344
  font-weight: 500;
345
  }
346
 
347
  /* Login button styling (from homepage) */
348
- .gemini-login-button {
349
  color: var(--gakr-blue);
350
  text-decoration: none;
351
  background: transparent;
@@ -356,12 +356,31 @@
356
  transition: background-color 0.2s;
357
  }
358
 
359
- .gemini-login-button:hover {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
  background-color: rgba(66, 133, 244, 0.1);
361
  }
362
 
363
  /* Login prompt modal (keep existing) */
364
- .gemini-login-prompt {
365
  position: fixed;
366
  top: 0;
367
  left: 0;
@@ -377,12 +396,12 @@
377
  transition: opacity 0.3s, visibility 0.3s;
378
  }
379
 
380
- .gemini-login-prompt.show {
381
  opacity: 1;
382
  visibility: visible;
383
  }
384
 
385
- .gemini-login-prompt-content {
386
  background-color: var(--bs-body-bg);
387
  border-radius: 12px;
388
  padding: 1.5rem;
@@ -390,21 +409,21 @@
390
  width: 90%;
391
  }
392
 
393
- .gemini-login-prompt-title {
394
  font-size: 1.25rem;
395
  font-weight: 500;
396
  margin-bottom: 1rem;
397
  color: var(--gakr-blue);
398
  }
399
 
400
- .gemini-login-prompt-buttons {
401
  display: flex;
402
  flex-direction: column;
403
  gap: 0.75rem;
404
  margin-top: 1.5rem;
405
  }
406
 
407
- .gemini-button-primary {
408
  background-color: var(--gakr-blue);
409
  color: white;
410
  border: none;
@@ -417,11 +436,11 @@
417
  transition: background-color 0.2s;
418
  }
419
 
420
- .gemini-button-primary:hover {
421
  background-color: #3b78e7;
422
  }
423
 
424
- .gemini-button-secondary {
425
  background-color: transparent;
426
  color: var(--gakr-blue);
427
  border: 1px solid var(--gakr-blue);
@@ -434,11 +453,11 @@
434
  transition: background-color 0.2s;
435
  }
436
 
437
- .gemini-button-secondary:hover {
438
  background-color: rgba(66, 133, 244, 0.1);
439
  }
440
 
441
- .gemini-button-text {
442
  background-color: transparent;
443
  color: var(--bs-body-color);
444
  border: none;
@@ -451,7 +470,7 @@
451
  transition: background-color 0.2s;
452
  }
453
 
454
- .gemini-button-text:hover {
455
  background-color: var(--bs-tertiary-bg);
456
  }
457
 
@@ -586,62 +605,62 @@
586
  </style>
587
  </head>
588
  <body>
589
- <div class="gemini-chat-layout">
590
- <header class="gemini-chat-header">
591
- <div class="gemini-logo-area">
592
- <a href="/" class="gemini-brand-logo">
593
  <i class="fas fa-robot" style="color: var(--gakr-blue);"></i>
594
  </a>
595
- <a href="/" class="gemini-brand-text" style="text-decoration: none;">GAKR AI</a>
596
  </div>
597
 
598
- <div class="gemini-nav-controls">
599
- <a href="auth.html" class="gemini-login-button">Sign in / Register</a>
600
  </div>
601
  </header>
602
 
603
- <div class="gemini-chat-wrapper">
604
- <div class="gemini-chat-container" id="chatContainer">
605
- <div class="gemini-welcome" id="welcomeMessage">
606
- <div class="gemini-welcome-icon">
607
  <i class="fas fa-robot"></i>
608
  </div>
609
- <h1 class="gemini-welcome-title">How can I help you today?</h1>
610
  <p>I'm GAKR AI, your AI assistant. Ask me anything!</p>
611
  </div>
612
 
613
- <div class="gemini-message gemini-message-ai d-none" id="initialMessage">
614
- <div class="gemini-message-header">
615
- <div class="gemini-message-avatar">
616
  <i class="fas fa-robot"></i>
617
  </div>
618
  <span>GAKR AI</span>
619
  </div>
620
- <div class="gemini-message-content">Hello! I'm GAKR AI How can I help you today?
621
  </div>
622
  </div>
623
 
624
- <div class="gemini-typing d-none" id="typingIndicator">
625
- <div class="gemini-typing-dot"></div>
626
- <div class="gemini-typing-dot"></div>
627
- <div class="gemini-typing-dot"></div>
628
  </div>
629
  </div>
630
 
631
- <div class="gemini-prompt-area">
632
- <div class="gemini-input-container" id="inputContainer">
633
- <div class="gemini-action-button" id="addButton">
634
  <i class="fas fa-plus"></i>
635
  </div>
636
  <div class="attached-file-preview-container" id="attachedFilePreviewContainer">
637
  </div>
638
- <textarea class="gemini-input" id="userInput" placeholder="Message GAKR AI..." rows="1"></textarea>
639
 
640
- <div class="gemini-input-actions">
641
- <div class="gemini-action-button">
642
  <i class="fas fa-microphone"></i>
643
  </div>
644
- <div class="gemini-action-button gemini-submit-button disabled" id="submitButton">
645
  <i class="fas fa-arrow-right"></i>
646
  </div>
647
  </div>
@@ -671,14 +690,14 @@
671
  </div>
672
  </div>
673
 
674
- <div class="gemini-login-prompt" id="loginPrompt">
675
- <div class="gemini-login-prompt-content">
676
- <div class="gemini-login-prompt-title">Continue with GAKR AI</div>
677
  <p>You've had 5 conversations with GAKR AI. Would you like to create an account to save your history?</p>
678
- <div class="gemini-login-prompt-buttons">
679
- <a href="login.html" class="gemini-button-primary">Sign in</a>
680
- <a href="/login?signup=true" class="gemini-button-secondary">Create account</a>
681
- <button type="button" class="gemini-button-text" id="continueGuest">Continue as guest</button>
682
  </div>
683
  </div>
684
  </div>
@@ -706,8 +725,20 @@ document.addEventListener('DOMContentLoaded', function() {
706
  let messageCount = 0;
707
  let attachedFiles = [];
708
 
709
- // Simple login flag (set in auth flow: localStorage.setItem("gakr_is_logged_in","true"))
710
- const isLoggedIn = localStorage.getItem("gakr_is_logged_in") === "true";
 
 
 
 
 
 
 
 
 
 
 
 
711
 
712
  // --- Textarea Auto-Grow & Scroll Logic (original, kept) ---
713
  const style = getComputedStyle(userInput);
@@ -717,7 +748,7 @@ document.addEventListener('DOMContentLoaded', function() {
717
 
718
  const initialInputContainerPaddingTop = parseFloat(getComputedStyle(inputContainer).paddingTop);
719
  const initialInputContainerPaddingBottom = parseFloat(getComputedStyle(inputContainer).paddingBottom);
720
- const initialActionButtonsHeight = document.querySelector('.gemini-input-actions').offsetHeight;
721
 
722
  function autoGrowTextarea() {
723
  if (!userInput) {
@@ -775,6 +806,14 @@ document.addEventListener('DOMContentLoaded', function() {
775
  }
776
  });
777
 
 
 
 
 
 
 
 
 
778
  // Existing “continue as guest” modal
779
  continueGuest.addEventListener('click', function() {
780
  loginPrompt.classList.remove('show');
@@ -957,7 +996,7 @@ document.addEventListener('DOMContentLoaded', function() {
957
  });
958
 
959
  loginBtn.addEventListener('click', function () {
960
- window.location.href = 'auth.html';
961
  });
962
 
963
  buttonRow.appendChild(skipBtn);
@@ -1006,7 +1045,7 @@ document.addEventListener('DOMContentLoaded', function() {
1006
  scrollToBottom();
1007
 
1008
  const formData = new FormData();
1009
- formData.append('api_key', 'gakr-ai-2025-secret');
1010
  formData.append('prompt', text);
1011
 
1012
  if (filesSnapshot.length > 0) {
@@ -1059,13 +1098,13 @@ document.addEventListener('DOMContentLoaded', function() {
1059
 
1060
  function addMessage(text, type, streaming = false) {
1061
  const messageDiv = document.createElement('div');
1062
- messageDiv.className = `gemini-message gemini-message-${type}`;
1063
 
1064
  const headerDiv = document.createElement('div');
1065
- headerDiv.className = 'gemini-message-header';
1066
 
1067
  const avatarDiv = document.createElement('div');
1068
- avatarDiv.className = 'gemini-message-avatar';
1069
 
1070
  const avatarIcon = document.createElement('i');
1071
  avatarIcon.className = type === 'user' ? 'fas fa-user' : 'fas fa-robot';
@@ -1074,7 +1113,7 @@ document.addEventListener('DOMContentLoaded', function() {
1074
  nameSpan.textContent = type === 'user' ? 'You' : 'GAKR AI';
1075
 
1076
  const contentDiv = document.createElement('div');
1077
- contentDiv.className = 'gemini-message-content';
1078
  if (!streaming) {
1079
  if (type === 'ai') {
1080
  updateAIMessage(contentDiv, text);
@@ -1111,7 +1150,7 @@ document.addEventListener('DOMContentLoaded', function() {
1111
 
1112
  // Create toggle button
1113
  const toggleButton = document.createElement('button');
1114
- toggleButton.className = 'gemini-thinking-toggle';
1115
  toggleButton.textContent = 'hide thinking';
1116
  toggleButton.onclick = function() {
1117
  const thinkingDiv = this.nextElementSibling;
@@ -1126,7 +1165,7 @@ document.addEventListener('DOMContentLoaded', function() {
1126
 
1127
  // Create thinking content div
1128
  const thinkingDiv = document.createElement('div');
1129
- thinkingDiv.className = 'gemini-thinking-content';
1130
  thinkingDiv.textContent = thinking;
1131
 
1132
  contentDiv.appendChild(toggleButton);
 
29
  }
30
 
31
  /* --- Base Chat Layout --- */
32
+ .gakr-chat-layout {
33
  display: flex;
34
  flex-direction: column;
35
  height: 100vh;
 
38
  color: var(--bs-body-color); /* Use BS var */
39
  }
40
 
41
+ .gakr-chat-header {
42
  padding: 0.75rem 1.5rem;
43
  display: flex;
44
  justify-content: space-between;
 
47
  height: 64px;
48
  }
49
 
50
+ .gakr-logo-area {
51
  display: flex;
52
  align-items: center;
53
  gap: 0.75rem;
54
  }
55
 
56
+ .gakr-brand-logo {
57
  width: 24px;
58
  height: 24px;
59
  display: flex;
 
61
  justify-content: center;
62
  }
63
 
64
+ .gakr-brand-text {
65
  font-size: 1.25rem;
66
  font-weight: 500;
67
  color: var(--gakr-blue);
68
  }
69
 
70
+ .gakr-chat-wrapper {
71
  flex: 1;
72
  overflow: hidden;
73
  position: relative;
 
75
  flex-direction: column;
76
  }
77
 
78
+ .gakr-chat-container {
79
  flex: 1;
80
  overflow-y: auto;
81
  padding: 1rem;
 
87
  margin: 0 auto;
88
  }
89
 
90
+ .gakr-prompt-area {
91
  padding: 1rem;
92
  border-top: 1px solid var(--bs-border-color);
93
  width: 100%;
 
98
  /* --- END MODIFIED CSS --- */
99
  }
100
 
101
+ .gakr-message {
102
  display: flex;
103
  flex-direction: column;
104
  gap: 0.5rem;
105
  max-width: 90%;
106
  }
107
 
108
+ .gakr-message-user {
109
  align-self: flex-end;
110
  }
111
 
112
+ .gakr-message-ai {
113
  align-self: flex-start;
114
  }
115
 
116
+ .gakr-message-header {
117
  display: flex;
118
  align-items: center;
119
  gap: 0.5rem;
 
121
  color: var(--bs-secondary-color);
122
  }
123
 
124
+ .gakr-message-avatar {
125
  width: 24px;
126
  height: 24px;
127
  border-radius: 50%;
 
131
  font-size: 0.75rem;
132
  }
133
 
134
+ .gakr-message-content {
135
  padding: 1rem;
136
  border-radius: 12px;
137
  line-height: 1.5;
 
140
  word-break: break-word;
141
  }
142
 
143
+ .gakr-message-user .gakr-message-content {
144
  background-color: var(--bs-tertiary-bg);
145
  border-top-right-radius: 4px;
146
  }
147
 
148
+ .gakr-message-ai .gakr-message-content {
149
  background-color: rgba(66, 133, 244, 0.1);
150
  border-top-left-radius: 4px;
151
  }
152
 
153
+ .gakr-message-user .gakr-message-avatar {
154
  background-color: var(--bs-tertiary-bg);
155
  }
156
 
157
+ .gakr-message-ai .gakr-message-avatar {
158
  background-color: rgba(66, 133, 244, 0.2);
159
  color: var(--gakr-blue);
160
  }
161
 
162
  /* --- MODIFIED CSS FOR INPUT/TEXTAREA AND ACTIONS --- */
163
+ .gakr-input-container {
164
  position: relative; /* Keep relative */
165
  border-radius: 24px;
166
  border: 1px solid var(--bs-border-color);
 
178
  }
179
 
180
  /* Style when the container or its children are focused */
181
+ .gakr-input-container:focus-within {
182
  border-color: var(--gakr-blue);
183
  box-shadow: 0 1px 8px rgba(66, 133, 244, 0.2);
184
  background: var(--bs-body-bg);
185
  }
186
 
187
  /* Style the textarea (#userInput) */
188
+ .gakr-input { /* Targets the textarea with id="userInput" */
189
  flex: 1; /* Textarea takes available space */
190
  border: none;
191
  background: transparent;
 
209
  transition: height 0.3s ease-in-out; /* Animate height change */
210
  }
211
 
212
+ .gakr-input:focus {
213
  outline: none;
214
  }
215
 
216
 
217
+ .gakr-input-actions {
218
  display: flex;
219
  /* --- MODIFIED: align-items to flex-end for textarea --- */
220
  align-items: flex-end;
 
226
  flex-shrink: 0; /* Prevent shrinking */
227
  }
228
 
229
+ /* Reusing .gakr-action-button styles for new '+' button */
230
+ .gakr-action-button {
231
  width: 36px;
232
  height: 36px;
233
  border-radius: 50%;
 
240
  transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
241
  }
242
 
243
+ .gakr-action-button:hover {
244
  background-color: var(--bs-tertiary-bg); /* Use BS var */
245
  color: var(--bs-body-color); /* Darken icon color slightly on hover */
246
  }
247
 
248
+ .gakr-action-button:active {
249
  transform: scale(0.95); /* Slight press effect */
250
  background-color: var(--bs-tertiary-bg); /* Keep hover bg on active */
251
  color: var(--bs-body-color);
252
  transition: background-color 0s, transform 0.1s; /* Make feedback immediate */
253
  }
254
 
255
+ .gakr-submit-button {
256
  color: var(--gakr-blue); /* Use CSS var */
257
  }
258
 
259
  /* Specific styles for submit button hover/active */
260
+ .gakr-submit-button:hover {
261
  background-color: var(--gakr-blue-light); /* Light blue background on hover */
262
  color: var(--gakr-blue-dark); /* Darker blue icon on hover */
263
  }
264
 
265
+ .gakr-submit-button:active {
266
  transform: scale(0.95); /* Slight press effect */
267
  background-color: var(--gakr-blue-light);
268
  color: var(--gakr-blue-dark);
269
  transition: background-color 0s, transform 0.1s;
270
  }
271
 
272
+ .gakr-submit-button.disabled {
273
  opacity: 0.5;
274
  cursor: default;
275
  /* Ensure transitions are off when disabled to avoid weird states */
276
  transition: none;
277
  }
278
 
279
+ .gakr-submit-button.disabled:hover {
280
  background-color: transparent;
281
  color: var(--gakr-blue); /* Keep original color when disabled */
282
  }
283
  /* --- END MODIFIED CSS --- */
284
 
285
 
286
+ .gakr-typing {
287
  display: inline-flex;
288
  align-items: center;
289
  gap: 4px;
 
294
  align-self: flex-end;
295
  }
296
 
297
+ .gakr-typing-dot {
298
  width: 6px;
299
  height: 6px;
300
  border-radius: 50%;
 
302
  animation: typing 1.3s infinite ease-in-out;
303
  }
304
 
305
+ .gakr-typing-dot:nth-child(1) { animation-delay: 0s; }
306
+ .gakr-typing-dot:nth-child(2) { animation-delay: 0.2s; }
307
+ .gakr-typing-dot:nth-child(3) { animation-delay: 0.4s; }
308
 
309
+ .gakr-thinking-toggle {
310
  background: none;
311
  border: none;
312
  color: var(--gakr-blue);
 
317
  text-decoration: underline;
318
  }
319
 
320
+ .gakr-thinking-content {
321
  display: block;
322
  margin-top: 0.5rem;
323
  padding: 0.5rem;
 
326
  font-style: italic;
327
  }
328
 
329
+ .gakr-welcome {
330
  text-align: center;
331
  max-width: 600px;
332
  margin: 4rem auto;
333
  }
334
 
335
+ .gakr-welcome-icon {
336
  font-size: 3rem;
337
  margin-bottom: 1.5rem;
338
  color: var(--gakr-blue);
339
  }
340
 
341
+ .gakr-welcome-title {
342
  font-size: 2rem;
343
  margin-bottom: 1rem;
344
  font-weight: 500;
345
  }
346
 
347
  /* Login button styling (from homepage) */
348
+ .gakr-login-button {
349
  color: var(--gakr-blue);
350
  text-decoration: none;
351
  background: transparent;
 
356
  transition: background-color 0.2s;
357
  }
358
 
359
+ .gakr-login-button:hover {
360
+ background-color: rgba(66, 133, 244, 0.1);
361
+ }
362
+
363
+ /* Profile button styling */
364
+ .gakr-profile-button {
365
+ color: var(--gakr-blue);
366
+ text-decoration: none;
367
+ background: transparent;
368
+ border: 1px solid var(--gakr-blue);
369
+ padding: 0.5rem 1rem;
370
+ border-radius: 50px;
371
+ font-size: 0.9rem;
372
+ transition: background-color 0.2s;
373
+ display: inline-flex;
374
+ align-items: center;
375
+ gap: 0.5rem;
376
+ }
377
+
378
+ .gakr-profile-button:hover {
379
  background-color: rgba(66, 133, 244, 0.1);
380
  }
381
 
382
  /* Login prompt modal (keep existing) */
383
+ .gakr-login-prompt {
384
  position: fixed;
385
  top: 0;
386
  left: 0;
 
396
  transition: opacity 0.3s, visibility 0.3s;
397
  }
398
 
399
+ .gakr-login-prompt.show {
400
  opacity: 1;
401
  visibility: visible;
402
  }
403
 
404
+ .gakr-login-prompt-content {
405
  background-color: var(--bs-body-bg);
406
  border-radius: 12px;
407
  padding: 1.5rem;
 
409
  width: 90%;
410
  }
411
 
412
+ .gakr-login-prompt-title {
413
  font-size: 1.25rem;
414
  font-weight: 500;
415
  margin-bottom: 1rem;
416
  color: var(--gakr-blue);
417
  }
418
 
419
+ .gakr-login-prompt-buttons {
420
  display: flex;
421
  flex-direction: column;
422
  gap: 0.75rem;
423
  margin-top: 1.5rem;
424
  }
425
 
426
+ .gakr-button-primary {
427
  background-color: var(--gakr-blue);
428
  color: white;
429
  border: none;
 
436
  transition: background-color 0.2s;
437
  }
438
 
439
+ .gakr-button-primary:hover {
440
  background-color: #3b78e7;
441
  }
442
 
443
+ .gakr-button-secondary {
444
  background-color: transparent;
445
  color: var(--gakr-blue);
446
  border: 1px solid var(--gakr-blue);
 
453
  transition: background-color 0.2s;
454
  }
455
 
456
+ .gakr-button-secondary:hover {
457
  background-color: rgba(66, 133, 244, 0.1);
458
  }
459
 
460
+ .gakr-button-text {
461
  background-color: transparent;
462
  color: var(--bs-body-color);
463
  border: none;
 
470
  transition: background-color 0.2s;
471
  }
472
 
473
+ .gakr-button-text:hover {
474
  background-color: var(--bs-tertiary-bg);
475
  }
476
 
 
605
  </style>
606
  </head>
607
  <body>
608
+ <div class="gakr-chat-layout">
609
+ <header class="gakr-chat-header">
610
+ <div class="gakr-logo-area">
611
+ <a href="/" class="gakr-brand-logo">
612
  <i class="fas fa-robot" style="color: var(--gakr-blue);"></i>
613
  </a>
614
+ <a href="/" class="gakr-brand-text" style="text-decoration: none;">GAKR AI</a>
615
  </div>
616
 
617
+ <div class="gakr-nav-controls">
618
+ <a href="/auth" class="gakr-login-button">Sign in / Register</a>
619
  </div>
620
  </header>
621
 
622
+ <div class="gakr-chat-wrapper">
623
+ <div class="gakr-chat-container" id="chatContainer">
624
+ <div class="gakr-welcome" id="welcomeMessage">
625
+ <div class="gakr-welcome-icon">
626
  <i class="fas fa-robot"></i>
627
  </div>
628
+ <h1 class="gakr-welcome-title">How can I help you today?</h1>
629
  <p>I'm GAKR AI, your AI assistant. Ask me anything!</p>
630
  </div>
631
 
632
+ <div class="gakr-message gakr-message-ai d-none" id="initialMessage">
633
+ <div class="gakr-message-header">
634
+ <div class="gakr-message-avatar">
635
  <i class="fas fa-robot"></i>
636
  </div>
637
  <span>GAKR AI</span>
638
  </div>
639
+ <div class="gakr-message-content">Hello! I'm GAKR AI How can I help you today?
640
  </div>
641
  </div>
642
 
643
+ <div class="gakr-typing d-none" id="typingIndicator">
644
+ <div class="gakr-typing-dot"></div>
645
+ <div class="gakr-typing-dot"></div>
646
+ <div class="gakr-typing-dot"></div>
647
  </div>
648
  </div>
649
 
650
+ <div class="gakr-prompt-area">
651
+ <div class="gakr-input-container" id="inputContainer">
652
+ <div class="gakr-action-button" id="addButton">
653
  <i class="fas fa-plus"></i>
654
  </div>
655
  <div class="attached-file-preview-container" id="attachedFilePreviewContainer">
656
  </div>
657
+ <textarea class="gakr-input" id="userInput" placeholder="Message GAKR AI..." rows="1"></textarea>
658
 
659
+ <div class="gakr-input-actions">
660
+ <div class="gakr-action-button">
661
  <i class="fas fa-microphone"></i>
662
  </div>
663
+ <div class="gakr-action-button gakr-submit-button disabled" id="submitButton">
664
  <i class="fas fa-arrow-right"></i>
665
  </div>
666
  </div>
 
690
  </div>
691
  </div>
692
 
693
+ <div class="gakr-login-prompt" id="loginPrompt">
694
+ <div class="gakr-login-prompt-content">
695
+ <div class="gakr-login-prompt-title">Continue with GAKR AI</div>
696
  <p>You've had 5 conversations with GAKR AI. Would you like to create an account to save your history?</p>
697
+ <div class="gakr-login-prompt-buttons">
698
+ <a href="login.html" class="gakr-button-primary">Sign in / Register</a>
699
+ <a href="/login?signup=true" class="gakr-button-secondary">Create account</a>
700
+ <button type="button" class="gakr-button-text" id="continueGuest">Continue as guest</button>
701
  </div>
702
  </div>
703
  </div>
 
725
  let messageCount = 0;
726
  let attachedFiles = [];
727
 
728
+ // Simple login flag (set in auth flow: localStorage.setItem("isLoggedIn","true"))
729
+ const isLoggedIn = localStorage.getItem("isLoggedIn") === "true";
730
+
731
+ // Update header based on login status
732
+ updateHeader();
733
+
734
+ function updateHeader() {
735
+ const navControls = document.querySelector('.gakr-nav-controls');
736
+ if (isLoggedIn) {
737
+ navControls.innerHTML = '<a href="/profile" class="gakr-profile-button"><i class="fas fa-user"></i> Profile</a>';
738
+ } else {
739
+ navControls.innerHTML = '<a href="/auth" class="gakr-login-button">Sign in / Register</a>';
740
+ }
741
+ }
742
 
743
  // --- Textarea Auto-Grow & Scroll Logic (original, kept) ---
744
  const style = getComputedStyle(userInput);
 
748
 
749
  const initialInputContainerPaddingTop = parseFloat(getComputedStyle(inputContainer).paddingTop);
750
  const initialInputContainerPaddingBottom = parseFloat(getComputedStyle(inputContainer).paddingBottom);
751
+ const initialActionButtonsHeight = document.querySelector('.gakr-input-actions').offsetHeight;
752
 
753
  function autoGrowTextarea() {
754
  if (!userInput) {
 
806
  }
807
  });
808
 
809
+ // Handle microphone button click
810
+ const microphoneButton = document.querySelector('.gakr-input-actions .gakr-action-button:first-child');
811
+ if (microphoneButton) {
812
+ microphoneButton.addEventListener('click', function() {
813
+ alert('Voice input feature is currently in development.');
814
+ });
815
+ }
816
+
817
  // Existing “continue as guest” modal
818
  continueGuest.addEventListener('click', function() {
819
  loginPrompt.classList.remove('show');
 
996
  });
997
 
998
  loginBtn.addEventListener('click', function () {
999
+ window.location.href = '/auth';
1000
  });
1001
 
1002
  buttonRow.appendChild(skipBtn);
 
1045
  scrollToBottom();
1046
 
1047
  const formData = new FormData();
1048
+
1049
  formData.append('prompt', text);
1050
 
1051
  if (filesSnapshot.length > 0) {
 
1098
 
1099
  function addMessage(text, type, streaming = false) {
1100
  const messageDiv = document.createElement('div');
1101
+ messageDiv.className = `gakr-message gakr-message-${type}`;
1102
 
1103
  const headerDiv = document.createElement('div');
1104
+ headerDiv.className = 'gakr-message-header';
1105
 
1106
  const avatarDiv = document.createElement('div');
1107
+ avatarDiv.className = 'gakr-message-avatar';
1108
 
1109
  const avatarIcon = document.createElement('i');
1110
  avatarIcon.className = type === 'user' ? 'fas fa-user' : 'fas fa-robot';
 
1113
  nameSpan.textContent = type === 'user' ? 'You' : 'GAKR AI';
1114
 
1115
  const contentDiv = document.createElement('div');
1116
+ contentDiv.className = 'gakr-message-content';
1117
  if (!streaming) {
1118
  if (type === 'ai') {
1119
  updateAIMessage(contentDiv, text);
 
1150
 
1151
  // Create toggle button
1152
  const toggleButton = document.createElement('button');
1153
+ toggleButton.className = 'gakr-thinking-toggle';
1154
  toggleButton.textContent = 'hide thinking';
1155
  toggleButton.onclick = function() {
1156
  const thinkingDiv = this.nextElementSibling;
 
1165
 
1166
  // Create thinking content div
1167
  const thinkingDiv = document.createElement('div');
1168
+ thinkingDiv.className = 'gakr-thinking-content';
1169
  thinkingDiv.textContent = thinking;
1170
 
1171
  contentDiv.appendChild(toggleButton);
profile.html ADDED
@@ -0,0 +1,2090 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en" data-bs-theme="dark">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>GAKR AI - Profile</title>
7
+ <script>
8
+ // Immediate check for login status
9
+ if (localStorage.getItem('isLoggedIn') !== 'true') {
10
+ window.location.href = '/auth';
11
+ }
12
+ </script>
13
+ <link rel="stylesheet" href="https://cdn.replit.com/agent/bootstrap-agent-dark-theme.min.css">
14
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
15
+ <style>
16
+ /*
17
+ ========================================
18
+ Base & Layout Styles
19
+ ========================================
20
+ */
21
+ :root {
22
+ --gakr-blue: #007bff; /* A vibrant blue for accents */
23
+ --gakr-blue-dark: #0056b3; /* Darker shade for hover/active states */
24
+ --gakr-blue-light: #e0f0ff; /* Lighter shade for selected states */
25
+ --gakr-blue-rgb: 0, 123, 255; /* RGB for translucent backgrounds */
26
+
27
+ /* Dark theme specifics (from replit's bootstrap-agent-dark-theme.min.css for consistency) */
28
+ --bs-body-bg: #1a1a1a; /* Dark background */
29
+ --bs-body-color: #f0f0f0; /* Light text */
30
+ --bs-primary: var(--gakr-blue);
31
+ --bs-secondary: #6c757d;
32
+ --bs-secondary-bg: #2a2a2a; /* Slightly lighter dark background */
33
+ --bs-tertiary-bg: #3a3a3a; /* Even lighter dark background for elements */
34
+ --bs-border-color: #4a4a4a; /* Border color */
35
+ --bs-link-color: var(--gakr-blue);
36
+ --bs-link-hover-color: var(--gakr-blue-dark);
37
+ --bs-heading-color: #f8f9fa; /* Lighter headings */
38
+ --bs-tertiary-color: #adb5bd; /* For less prominent text */
39
+ --bs-success: #28a745;
40
+ --bs-info: #17a2b8;
41
+ --bs-warning: #ffc107;
42
+ --bs-danger: #dc3545;
43
+ }
44
+
45
+ body {
46
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
47
+ background-color: var(--bs-body-bg);
48
+ color: var(--bs-body-color);
49
+ margin: 0;
50
+ padding-top: 64px;
51
+ display: flex;
52
+ flex-direction: column;
53
+ min-height: 100vh;
54
+ }
55
+
56
+ .gakr-layout {
57
+ display: flex;
58
+ flex-direction: column;
59
+ min-height: 100vh;
60
+ width: 100%;
61
+ max-width: 1200px; /* Max width for content */
62
+ margin: 0 auto; /* Center the layout */
63
+ box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); /* Subtle shadow */
64
+ background-color: var(--bs-body-bg); /* Ensure content area matches body bg */
65
+ }
66
+
67
+ /*
68
+ ========================================
69
+ Header Styles
70
+ ========================================
71
+ */
72
+ .gakr-chat-header {
73
+ padding: 0.75rem 1.5rem;
74
+ display: flex;
75
+ justify-content: space-between;
76
+ align-items: center;
77
+ border-bottom: 1px solid var(--bs-border-color);
78
+ height: 64px;
79
+ position: fixed;
80
+ top: 0;
81
+ left: 0;
82
+ right: 0;
83
+ width: 100%;
84
+ background-color: var(--bs-body-bg);
85
+ z-index: 10;
86
+ }
87
+
88
+ .gakr-logo-area {
89
+ display: flex;
90
+ align-items: center;
91
+ gap: 0.75rem;
92
+ }
93
+
94
+ .gakr-brand-logo {
95
+ width: 24px;
96
+ height: 24px;
97
+ display: flex;
98
+ align-items: center;
99
+ justify-content: center;
100
+ }
101
+
102
+ .gakr-brand-text {
103
+ font-size: 1.25rem;
104
+ font-weight: 500;
105
+ color: var(--gakr-blue);
106
+ }
107
+
108
+ .gakr-nav-controls {
109
+ display: flex;
110
+ align-items: center;
111
+ }
112
+
113
+ .gakr-login-button {
114
+ color: var(--gakr-blue);
115
+ text-decoration: none;
116
+ background: transparent;
117
+ border: 1px solid var(--gakr-blue);
118
+ padding: 0.5rem 1rem;
119
+ border-radius: 50px;
120
+ font-size: 0.9rem;
121
+ transition: background-color 0.2s;
122
+ }
123
+
124
+ .gakr-login-button:hover {
125
+ background-color: rgba(66, 133, 244, 0.1);
126
+ }
127
+
128
+ .gakr-profile-button {
129
+ color: var(--gakr-blue);
130
+ text-decoration: none;
131
+ background: transparent;
132
+ border: none;
133
+ padding: 0.5rem 1rem;
134
+ border-radius: 50px;
135
+ font-size: 0.9rem;
136
+ transition: background-color 0.2s;
137
+ display: flex;
138
+ align-items: center;
139
+ gap: 0.5rem;
140
+ }
141
+
142
+ .gakr-profile-button:hover {
143
+ background-color: rgba(66, 133, 244, 0.1);
144
+ }
145
+
146
+ /*
147
+ ========================================
148
+ Profile Button & Dropdown
149
+ ========================================
150
+ */
151
+ .profile-nav-controls {
152
+ position: relative;
153
+ z-index: 1001; /* Ensure dropdown is above other content */
154
+ }
155
+
156
+ .profile-button {
157
+ width: 40px;
158
+ height: 40px;
159
+ border-radius: 50%;
160
+ background-color: var(--gakr-blue);
161
+ color: white;
162
+ display: flex;
163
+ justify-content: center;
164
+ align-items: center;
165
+ font-size: 1.2rem;
166
+ font-weight: bold;
167
+ cursor: pointer;
168
+ transition: background-color 0.2s ease;
169
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
170
+ }
171
+
172
+ .profile-button:hover {
173
+ background-color: var(--gakr-blue-dark);
174
+ }
175
+
176
+ .profile-dropdown-menu {
177
+ position: absolute;
178
+ top: calc(100% + 10px);
179
+ right: 0;
180
+ background-color: var(--bs-tertiary-bg);
181
+ border: 1px solid var(--bs-border-color);
182
+ border-radius: 8px;
183
+ min-width: 180px;
184
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
185
+ opacity: 0;
186
+ visibility: hidden;
187
+ transform: translateY(-10px);
188
+ transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease;
189
+ padding: 0.5rem 0;
190
+ z-index: 100;
191
+ }
192
+
193
+ .profile-dropdown-menu.show {
194
+ opacity: 1;
195
+ visibility: visible;
196
+ transform: translateY(0);
197
+ }
198
+
199
+ .profile-dropdown-item {
200
+ display: block;
201
+ padding: 0.8rem 1.2rem;
202
+ color: var(--bs-body-color);
203
+ text-decoration: none;
204
+ font-size: 0.95rem;
205
+ transition: background-color 0.2s ease, color 0.2s ease;
206
+ }
207
+
208
+ .profile-dropdown-item:hover {
209
+ background-color: var(--bs-secondary-bg);
210
+ color: var(--gakr-blue);
211
+ }
212
+
213
+ /*
214
+ ========================================
215
+ Profile View Specific Styles
216
+ ========================================
217
+ */
218
+ .profile-view {
219
+ flex-grow: 1;
220
+ padding: 2rem;
221
+ background-color: var(--bs-body-bg); /* Main content background */
222
+ display: flex;
223
+ flex-direction: column;
224
+ gap: 2rem;
225
+ }
226
+
227
+ .profile-header-section {
228
+ display: flex;
229
+ flex-direction: column;
230
+ align-items: center;
231
+ text-align: center;
232
+ margin-bottom: 2rem;
233
+ padding-bottom: 1.5rem;
234
+ border-bottom: 1px solid var(--bs-border-color);
235
+ }
236
+
237
+ .profile-avatar-large {
238
+ width: 120px;
239
+ height: 120px;
240
+ border-radius: 50%;
241
+ background-color: var(--gakr-blue);
242
+ color: white;
243
+ display: flex;
244
+ justify-content: center;
245
+ align-items: center;
246
+ font-size: 4rem;
247
+ font-weight: bold;
248
+ margin-bottom: 1rem;
249
+ border: 4px solid var(--bs-tertiary-bg);
250
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
251
+ position: relative; /* For the edit icon */
252
+ overflow: hidden; /* To clip avatar images */
253
+ }
254
+
255
+ .profile-avatar-large img {
256
+ width: 100%;
257
+ height: 100%;
258
+ object-fit: cover;
259
+ border-radius: 50%;
260
+ }
261
+
262
+ .profile-avatar-edit-icon {
263
+ position: absolute;
264
+ bottom: 5px;
265
+ right: 5px;
266
+ background-color: var(--bs-tertiary-bg);
267
+ border-radius: 50%;
268
+ width: 30px;
269
+ height: 30px;
270
+ display: flex;
271
+ justify-content: center;
272
+ align-items: center;
273
+ cursor: pointer;
274
+ box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
275
+ transition: background-color 0.2s ease;
276
+ color: var(--bs-body-color);
277
+ }
278
+
279
+ .profile-avatar-edit-icon:hover {
280
+ background-color: var(--bs-secondary-bg);
281
+ color: var(--gakr-blue);
282
+ }
283
+
284
+ .profile-username-container {
285
+ display: flex;
286
+ align-items: center;
287
+ gap: 0.5rem;
288
+ margin-bottom: 0.5rem;
289
+ }
290
+
291
+ .profile-username {
292
+ font-size: 2.2rem;
293
+ font-weight: 700;
294
+ color: var(--bs-heading-color);
295
+ margin: 0;
296
+ line-height: 1;
297
+ }
298
+
299
+ .username-edit-icon, .section-edit-icon {
300
+ color: var(--bs-secondary-color);
301
+ cursor: pointer;
302
+ font-size: 1.1rem;
303
+ transition: color 0.2s ease;
304
+ }
305
+
306
+ .username-edit-icon:hover, .section-edit-icon:hover {
307
+ color: var(--gakr-blue);
308
+ }
309
+
310
+ .username-input-container {
311
+ display: none; /* Hidden by default, toggled by JS */
312
+ align-items: center;
313
+ gap: 0.5rem;
314
+ margin-bottom: 0.5rem;
315
+ width: 100%;
316
+ max-width: 300px;
317
+ }
318
+
319
+ .username-edit-input {
320
+ flex-grow: 1;
321
+ padding: 0.6rem 0.8rem;
322
+ border-radius: 8px;
323
+ border: 1px solid var(--bs-border-color);
324
+ background-color: var(--bs-secondary-bg);
325
+ color: var(--bs-body-color);
326
+ font-size: 1.1rem;
327
+ outline: none;
328
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);
329
+ }
330
+ .username-edit-input::placeholder {
331
+ color: var(--bs-tertiary-color);
332
+ }
333
+ .username-edit-input:focus {
334
+ border-color: var(--gakr-blue);
335
+ box-shadow: 0 0 0 0.25rem rgba(var(--gakr-blue-rgb), 0.25);
336
+ }
337
+
338
+
339
+ .username-save-button {
340
+ padding: 0.6rem 1.2rem;
341
+ border-radius: 8px;
342
+ background-color: var(--gakr-blue);
343
+ color: white;
344
+ border: none;
345
+ cursor: pointer;
346
+ font-size: 1rem;
347
+ transition: background-color 0.2s ease;
348
+ }
349
+
350
+ .username-save-button:hover {
351
+ background-color: var(--gakr-blue-dark);
352
+ }
353
+
354
+ .profile-email {
355
+ font-size: 1.1rem;
356
+ color: var(--bs-secondary-color);
357
+ margin: 0;
358
+ }
359
+
360
+ .profile-section {
361
+ background-color: var(--bs-secondary-bg);
362
+ border-radius: 12px;
363
+ padding: 1.5rem 2rem;
364
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
365
+ display: flex;
366
+ flex-direction: column;
367
+ gap: 1rem;
368
+ }
369
+
370
+ .profile-section-title {
371
+ font-size: 1.6rem;
372
+ color: var(--bs-heading-color);
373
+ margin-top: 0;
374
+ margin-bottom: 1rem;
375
+ padding-bottom: 0.5rem;
376
+ border-bottom: 1px solid var(--bs-border-color);
377
+ display: flex;
378
+ align-items: center;
379
+ gap: 0.75rem;
380
+ }
381
+
382
+ .profile-info-item {
383
+ display: flex;
384
+ justify-content: space-between;
385
+ align-items: center;
386
+ padding: 0.5rem 0;
387
+ border-bottom: 1px dashed var(--bs-border-color);
388
+ }
389
+ .profile-info-item:last-child {
390
+ border-bottom: none;
391
+ }
392
+
393
+ .profile-info-label {
394
+ font-weight: 500;
395
+ color: var(--bs-tertiary-color);
396
+ flex-basis: 30%; /* Adjust as needed */
397
+ }
398
+
399
+ .profile-info-value {
400
+ color: var(--bs-body-color);
401
+ text-align: right;
402
+ flex-basis: 70%; /* Adjust as needed */
403
+ }
404
+
405
+ /* About Me specific styles */
406
+ .profile-about-me-display {
407
+ color: var(--bs-body-color);
408
+ line-height: 1.6;
409
+ margin-bottom: 1rem;
410
+ background-color: var(--bs-body-bg);
411
+ border: 1px solid var(--bs-border-color);
412
+ border-radius: 8px;
413
+ padding: 1rem;
414
+ min-height: 80px;
415
+ overflow-y: auto;
416
+ }
417
+ .profile-about-me-display.placeholder {
418
+ color: var(--bs-tertiary-color);
419
+ font-style: italic;
420
+ }
421
+
422
+ .about-me-edit-icon {
423
+ margin-left: auto; /* Push icon to the right if title is flex */
424
+ }
425
+
426
+ .about-me-input-container {
427
+ display: none; /* Hidden by default */
428
+ flex-direction: column; /* Stack textarea and button */
429
+ gap: 0.75rem;
430
+ }
431
+
432
+ .about-me-edit-textarea {
433
+ width: 100%;
434
+ padding: 0.8rem;
435
+ border-radius: 8px;
436
+ border: 1px solid var(--bs-border-color);
437
+ background-color: var(--bs-body-bg);
438
+ color: var(--bs-body-color);
439
+ font-size: 1rem;
440
+ min-height: 120px;
441
+ resize: vertical;
442
+ outline: none;
443
+ box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);
444
+ }
445
+ .about-me-edit-textarea::placeholder {
446
+ color: var(--bs-tertiary-color);
447
+ }
448
+ .about-me-edit-textarea:focus {
449
+ border-color: var(--gakr-blue);
450
+ box-shadow: 0 0 0 0.25rem rgba(var(--gakr-blue-rgb), 0.25);
451
+ }
452
+
453
+ .about-me-save-button {
454
+ align-self: flex-end; /* Align to the right */
455
+ padding: 0.7rem 1.5rem;
456
+ border-radius: 8px;
457
+ background-color: var(--gakr-blue);
458
+ color: white;
459
+ border: none;
460
+ cursor: pointer;
461
+ font-size: 1rem;
462
+ transition: background-color 0.2s ease;
463
+ }
464
+
465
+ .about-me-save-button:hover {
466
+ background-color: var(--gakr-blue-dark);
467
+ }
468
+
469
+ /* Chat History Styles */
470
+ .profile-chat-history {
471
+ display: flex;
472
+ flex-direction: column;
473
+ gap: 1rem;
474
+ }
475
+
476
+ .chat-history-item {
477
+ background-color: var(--bs-body-bg);
478
+ border: 1px solid var(--bs-border-color);
479
+ border-radius: 12px;
480
+ padding: 1rem 1.2rem;
481
+ cursor: pointer;
482
+ transition: background-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease;
483
+ position: relative; /* For context menu positioning */
484
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
485
+ }
486
+
487
+ .chat-history-item:hover {
488
+ background-color: var(--bs-secondary-bg);
489
+ transform: translateY(-2px);
490
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
491
+ }
492
+
493
+ .chat-history-item .chat-title-wrapper {
494
+ display: flex;
495
+ align-items: center;
496
+ gap: 0.5rem;
497
+ margin-bottom: 0.5rem;
498
+ }
499
+
500
+ .chat-history-item .chat-title {
501
+ font-size: 1.15rem;
502
+ font-weight: 600;
503
+ color: var(--bs-heading-color);
504
+ flex-grow: 1; /* Allows title to take available space */
505
+ }
506
+
507
+ .chat-history-item .chat-meta {
508
+ font-size: 0.85rem;
509
+ color: var(--bs-secondary-color);
510
+ display: flex;
511
+ justify-content: space-between;
512
+ align-items: center;
513
+ margin-bottom: 0.5rem;
514
+ }
515
+
516
+ .empty-chat-history {
517
+ text-align: center;
518
+ color: var(--bs-tertiary-color);
519
+ font-style: italic;
520
+ padding: 2rem;
521
+ }
522
+
523
+ /* Profile Buttons Area */
524
+ .profile-buttons-area {
525
+ display: flex;
526
+ flex-direction: column;
527
+ align-items: center;
528
+ gap: 1rem;
529
+ margin-top: 2rem;
530
+ padding-top: 2rem;
531
+ border-top: 1px solid var(--bs-border-color);
532
+ }
533
+
534
+ .gakr-button-primary, .gakr-button-secondary {
535
+ display: inline-flex;
536
+ align-items: center;
537
+ justify-content: center;
538
+ padding: 0.8rem 1.5rem;
539
+ border-radius: 10px;
540
+ font-size: 1rem;
541
+ font-weight: 500;
542
+ text-decoration: none;
543
+ transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease;
544
+ width: 100%;
545
+ max-width: 300px;
546
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
547
+ }
548
+
549
+ .gakr-button-primary {
550
+ background-color: var(--gakr-blue);
551
+ color: white;
552
+ border: 1px solid var(--gakr-blue);
553
+ }
554
+
555
+ .gakr-button-primary:hover {
556
+ background-color: var(--gakr-blue-dark);
557
+ border-color: var(--gakr-blue-dark);
558
+ color: white;
559
+ }
560
+
561
+ .gakr-button-secondary {
562
+ background-color: transparent;
563
+ color: var(--gakr-blue);
564
+ border: 1px solid var(--gakr-blue);
565
+ }
566
+
567
+ .gakr-button-secondary:hover {
568
+ background-color: var(--gakr-blue-light);
569
+ color: var(--gakr-blue-dark);
570
+ border-color: var(--gakr-blue-dark);
571
+ }
572
+
573
+ .me-2 { /* Margin End (right) */
574
+ margin-right: 0.5rem;
575
+ }
576
+
577
+ /*
578
+ ========================================
579
+ Footer Styles
580
+ ========================================
581
+ */
582
+ .gakr-footer {
583
+ padding: 1rem 1.5rem;
584
+ background-color: var(--bs-tertiary-bg);
585
+ border-top: 1px solid var(--bs-border-color);
586
+ text-align: center;
587
+ font-size: 0.9rem;
588
+ color: var(--bs-secondary-color);
589
+ margin-top: auto; /* Push footer to the bottom */
590
+ }
591
+
592
+ /*
593
+ ========================================
594
+ Toast Notification Styles
595
+ ========================================
596
+ */
597
+ .toast-container {
598
+ position: fixed;
599
+ bottom: 20px;
600
+ left: 50%;
601
+ transform: translateX(-50%);
602
+ display: flex;
603
+ flex-direction: column;
604
+ gap: 10px;
605
+ z-index: 2000; /* Above all other content */
606
+ }
607
+
608
+ .gakr-toast {
609
+ background-color: var(--bs-tertiary-bg);
610
+ color: var(--bs-body-color);
611
+ padding: 12px 20px;
612
+ border-radius: 8px;
613
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
614
+ display: flex;
615
+ align-items: center;
616
+ gap: 10px;
617
+ min-width: 250px;
618
+ opacity: 0;
619
+ transform: translateY(20px);
620
+ transition: opacity 0.3s ease-out, transform 0.3s ease-out;
621
+ border: 1px solid var(--bs-border-color);
622
+ }
623
+
624
+ .gakr-toast.show {
625
+ opacity: 1;
626
+ transform: translateY(0);
627
+ }
628
+
629
+ .gakr-toast.hide {
630
+ opacity: 0;
631
+ transform: translateY(20px);
632
+ }
633
+
634
+ .gakr-toast .toast-icon {
635
+ font-size: 1.2rem;
636
+ }
637
+
638
+ .gakr-toast.success {
639
+ border-left: 5px solid var(--bs-success);
640
+ }
641
+ .gakr-toast.success .toast-icon {
642
+ color: var(--bs-success);
643
+ }
644
+
645
+ .gakr-toast.error {
646
+ border-left: 5px solid var(--bs-danger);
647
+ }
648
+ .gakr-toast.error .toast-icon {
649
+ color: var(--bs-danger);
650
+ }
651
+
652
+ .gakr-toast.warning {
653
+ border-left: 5px solid var(--bs-warning);
654
+ }
655
+ .gakr-toast.warning .toast-icon {
656
+ color: var(--bs-warning);
657
+ }
658
+
659
+ .gakr-toast.info {
660
+ border-left: 5px solid var(--bs-info);
661
+ }
662
+ .gakr-toast.info .toast-icon {
663
+ color: var(--bs-info);
664
+ }
665
+
666
+
667
+ /*
668
+ ========================================
669
+ Custom Modal Styles (Confirm/Prompt)
670
+ ========================================
671
+ */
672
+ .gakr-modal-overlay {
673
+ position: fixed;
674
+ top: 0;
675
+ left: 0;
676
+ width: 100%;
677
+ height: 100%;
678
+ background-color: rgba(0, 0, 0, 0.6); /* Semi-transparent dark overlay */
679
+ display: flex;
680
+ justify-content: center;
681
+ align-items: center;
682
+ z-index: 1500;
683
+ opacity: 0;
684
+ visibility: hidden;
685
+ transition: opacity 0.3s ease, visibility 0.3s ease;
686
+ }
687
+
688
+ .gakr-modal-overlay.show {
689
+ opacity: 1;
690
+ visibility: visible;
691
+ }
692
+
693
+ .gakr-modal-content {
694
+ background-color: var(--bs-body-bg);
695
+ border: 1px solid var(--bs-border-color);
696
+ border-radius: 12px;
697
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
698
+ padding: 2rem;
699
+ width: 90%;
700
+ max-width: 450px;
701
+ text-align: center;
702
+ transform: translateY(-20px) scale(0.95);
703
+ opacity: 0;
704
+ transition: transform 0.3s ease-out, opacity 0.3s ease-out;
705
+ }
706
+
707
+ .gakr-modal-overlay.show .gakr-modal-content {
708
+ transform: translateY(0) scale(1);
709
+ opacity: 1;
710
+ }
711
+
712
+ .gakr-modal-title {
713
+ font-size: 1.5rem;
714
+ font-weight: 600;
715
+ margin-bottom: 1rem;
716
+ color: var(--bs-heading-color);
717
+ }
718
+
719
+ .gakr-modal-message {
720
+ font-size: 1rem;
721
+ color: var(--bs-body-color);
722
+ margin-bottom: 1.5rem;
723
+ line-height: 1.5;
724
+ }
725
+
726
+ .gakr-modal-input {
727
+ width: calc(100% - 20px); /* Account for padding */
728
+ padding: 0.8rem 10px;
729
+ margin-bottom: 1.5rem;
730
+ border: 1px solid var(--bs-border-color);
731
+ border-radius: 8px;
732
+ background-color: var(--bs-secondary-bg);
733
+ color: var(--bs-body-color);
734
+ font-size: 1rem;
735
+ outline: none;
736
+ }
737
+
738
+ .gakr-modal-input:focus {
739
+ border-color: var(--gakr-blue);
740
+ box-shadow: 0 0 0 0.2rem rgba(var(--gakr-blue-rgb), 0.25);
741
+ }
742
+
743
+ .gakr-modal-buttons {
744
+ display: flex;
745
+ justify-content: center;
746
+ gap: 1rem;
747
+ }
748
+
749
+ .gakr-modal-buttons button {
750
+ padding: 0.7rem 1.5rem;
751
+ border-radius: 8px;
752
+ font-size: 1rem;
753
+ cursor: pointer;
754
+ transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out;
755
+ }
756
+
757
+ .confirm-ok, .prompt-submit {
758
+ background-color: var(--gakr-blue);
759
+ color: white;
760
+ border: none;
761
+ }
762
+
763
+ .confirm-ok:hover, .prompt-submit:hover {
764
+ background-color: var(--gakr-blue-dark);
765
+ }
766
+
767
+ .confirm-cancel, .prompt-cancel {
768
+ background-color: transparent;
769
+ color: var(--gakr-blue);
770
+ border: 1px solid var(--gakr-blue);
771
+ }
772
+
773
+ .confirm-cancel:hover, .prompt-cancel:hover {
774
+ background-color: var(--gakr-blue-light);
775
+ color: var(--gakr-blue-dark);
776
+ border-color: var(--gakr-blue-dark);
777
+ }
778
+
779
+ /*
780
+ ========================================
781
+ Advanced Features Styles (New)
782
+ ========================================
783
+ */
784
+ .chat-history-item.pinned {
785
+ background-color: var(--bs-secondary-bg);
786
+ border-left: 5px solid var(--gakr-blue);
787
+ padding-left: calc(1rem - 5px); /* Adjust padding due to border */
788
+ }
789
+ .pinned-icon {
790
+ color: var(--gakr-blue);
791
+ font-size: 0.9em;
792
+ margin-left: 0.5rem;
793
+ vertical-align: middle;
794
+ }
795
+
796
+ .chat-history-item .chat-tags {
797
+ font-size: 0.8rem;
798
+ color: var(--bs-secondary-color);
799
+ margin-top: 0.5rem;
800
+ }
801
+ .chat-history-item .chat-tags span {
802
+ background-color: rgba(var(--gakr-blue-rgb), 0.1);
803
+ color: var(--gakr-blue);
804
+ padding: 0.2rem 0.6rem;
805
+ border-radius: 12px; /* Pill shape */
806
+ margin-right: 0.4rem;
807
+ display: inline-block;
808
+ margin-bottom: 0.4rem; /* For multiple lines of tags */
809
+ white-space: nowrap; /* Prevent tags from breaking */
810
+ }
811
+ .chat-history-item .chat-tags span:last-child {
812
+ margin-right: 0;
813
+ }
814
+
815
+ .chat-history-search-container {
816
+ position: relative;
817
+ margin-bottom: 1rem;
818
+ width: 100%;
819
+ max-width: 400px; /* Limit search input width */
820
+ align-self: center; /* Center if flex container */
821
+ }
822
+ .chat-history-search-input {
823
+ width: 100%;
824
+ padding: 0.6rem 2.5rem 0.6rem 1rem; /* Adjust padding for icon */
825
+ border-radius: 8px;
826
+ border: 1px solid var(--bs-border-color);
827
+ background-color: var(--bs-body-bg);
828
+ color: var(--bs-body-color);
829
+ font-size: 0.95rem;
830
+ outline: none;
831
+ }
832
+ .chat-history-search-input:focus {
833
+ border-color: var(--gakr-blue);
834
+ box-shadow: 0 0 0 0.25rem rgba(var(--gakr-blue-rgb), 0.25);
835
+ }
836
+ .chat-history-search-icon {
837
+ position: absolute;
838
+ right: 1rem;
839
+ top: 50%;
840
+ transform: translateY(-50%);
841
+ color: var(--bs-secondary-color);
842
+ pointer-events: none; /* Make icon not interfere with input clicks */
843
+ }
844
+
845
+
846
+ .chat-history-sort-container {
847
+ margin-bottom: 1rem;
848
+ display: flex;
849
+ justify-content: flex-end; /* Align to the right */
850
+ align-items: center;
851
+ gap: 0.5rem;
852
+ font-size: 0.9rem;
853
+ color: var(--bs-secondary-color);
854
+ }
855
+ .chat-history-sort-select {
856
+ background-color: var(--bs-tertiary-bg);
857
+ color: var(--bs-body-color);
858
+ border: 1px solid var(--bs-border-color);
859
+ border-radius: 8px;
860
+ padding: 0.3rem 0.6rem;
861
+ font-size: 0.9rem;
862
+ cursor: pointer;
863
+ outline: none;
864
+ -webkit-appearance: none; /* Remove default arrow */
865
+ -moz-appearance: none;
866
+ appearance: none;
867
+ background-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23f0f0f0'%3E%3Cpath d='M7 10l5 5 5-5z'/%3E%3Csvg%3E");
868
+ background-repeat: no-repeat;
869
+ background-position: right 0.5rem center;
870
+ background-size: 1em;
871
+ }
872
+ .chat-history-sort-select:focus {
873
+ border-color: var(--gakr-blue);
874
+ box-shadow: 0 0 0 0.25rem rgba(var(--gakr-blue-rgb), 0.25);
875
+ }
876
+
877
+ .chat-history-last-active {
878
+ font-size: 0.75rem;
879
+ color: var(--bs-tertiary-color);
880
+ margin-left: auto; /* Push to the right */
881
+ }
882
+
883
+ /* Chat Context Menu */
884
+ .chat-context-menu {
885
+ position: fixed; /* Fixed position relative to viewport */
886
+ background-color: var(--bs-tertiary-bg);
887
+ border: 1px solid var(--bs-border-color);
888
+ border-radius: 8px;
889
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
890
+ min-width: 150px;
891
+ padding: 0.5rem 0;
892
+ z-index: 1002; /* Above profile dropdown */
893
+ opacity: 0;
894
+ visibility: hidden;
895
+ transform: scale(0.95);
896
+ transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s ease;
897
+ }
898
+
899
+ .chat-context-menu.show {
900
+ opacity: 1;
901
+ visibility: visible;
902
+ transform: scale(1);
903
+ }
904
+
905
+ .chat-context-menu-item {
906
+ display: flex;
907
+ align-items: center;
908
+ gap: 0.8rem;
909
+ padding: 0.8rem 1.2rem;
910
+ color: var(--bs-body-color);
911
+ text-decoration: none;
912
+ font-size: 0.9rem;
913
+ transition: background-color 0.2s ease, color 0.2s ease;
914
+ }
915
+
916
+ .chat-context-menu-item:hover {
917
+ background-color: var(--bs-secondary-bg);
918
+ color: var(--gakr-blue);
919
+ }
920
+
921
+ .chat-context-menu-item i {
922
+ width: 1.2rem; /* Align icons */
923
+ text-align: center;
924
+ }
925
+
926
+ /* Tag Selection Modal */
927
+ .tag-selection-modal-content {
928
+ background-color: var(--bs-body-bg);
929
+ border: 1px solid var(--bs-border-color);
930
+ border-radius: 12px;
931
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
932
+ padding: 1.5rem;
933
+ width: 90%;
934
+ max-width: 500px;
935
+ text-align: left;
936
+ transform: translateY(-20px) scale(0.95);
937
+ opacity: 0;
938
+ transition: transform 0.3s ease-out, opacity 0.3s ease-out;
939
+ }
940
+ .gakr-modal-overlay.show .tag-selection-modal-content {
941
+ transform: translateY(0) scale(1);
942
+ opacity: 1;
943
+ }
944
+ .tag-selection-modal-title {
945
+ font-size: 1.3rem;
946
+ font-weight: 600;
947
+ margin-bottom: 1rem;
948
+ color: var(--bs-heading-color);
949
+ padding-bottom: 0.5rem;
950
+ border-bottom: 1px solid var(--bs-border-color);
951
+ }
952
+ .tag-options-grid {
953
+ display: grid;
954
+ grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); /* Responsive grid */
955
+ gap: 0.75rem;
956
+ margin-bottom: 1.5rem;
957
+ }
958
+ .tag-option {
959
+ background-color: var(--bs-secondary-bg);
960
+ color: var(--bs-body-color);
961
+ border: 1px solid var(--bs-border-color);
962
+ border-radius: 8px;
963
+ padding: 0.5rem 0.8rem;
964
+ cursor: pointer;
965
+ text-align: center;
966
+ transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
967
+ font-size: 0.9rem;
968
+ display: flex;
969
+ justify-content: center;
970
+ align-items: center;
971
+ }
972
+ .tag-option:hover {
973
+ background-color: var(--bs-tertiary-bg);
974
+ border-color: var(--gakr-blue);
975
+ }
976
+ .tag-option.selected {
977
+ background-color: var(--gakr-blue);
978
+ color: white;
979
+ border-color: var(--gakr-blue-dark);
980
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
981
+ }
982
+ .tag-option.selected:hover {
983
+ background-color: var(--gakr-blue-dark);
984
+ border-color: var(--gakr-blue-dark);
985
+ }
986
+ .tag-actions {
987
+ display: flex;
988
+ justify-content: flex-end;
989
+ gap: 1rem;
990
+ padding-top: 1rem;
991
+ border-top: 1px solid var(--bs-border-color);
992
+ }
993
+ .tag-actions button {
994
+ padding: 0.6rem 1rem;
995
+ border-radius: 8px;
996
+ font-size: 1rem;
997
+ cursor: pointer;
998
+ transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out;
999
+ }
1000
+ .tag-actions .confirm-ok {
1001
+ background-color: var(--gakr-blue);
1002
+ color: white;
1003
+ border: none;
1004
+ }
1005
+ .tag-actions .confirm-ok:hover {
1006
+ background-color: var(--gakr-blue-dark);
1007
+ }
1008
+ .tag-actions .confirm-cancel {
1009
+ background-color: transparent;
1010
+ color: var(--gakr-blue);
1011
+ border: 1px solid var(--gakr-blue);
1012
+ }
1013
+ .tag-actions .confirm-cancel:hover {
1014
+ background-color: var(--gakr-blue-light);
1015
+ color: var(--gakr-blue-dark);
1016
+ border-color: var(--gakr-blue-dark);
1017
+ }
1018
+
1019
+
1020
+ /*
1021
+ ========================================
1022
+ Responsive Adjustments (Optional but Recommended)
1023
+ ========================================
1024
+ */
1025
+ @media (max-width: 768px) {
1026
+ .gakr-header {
1027
+ padding: 0.8rem 1rem;
1028
+ }
1029
+ .profile-view {
1030
+ padding: 1rem;
1031
+ gap: 1.5rem;
1032
+ }
1033
+ .profile-header-section {
1034
+ margin-bottom: 1.5rem;
1035
+ }
1036
+ .profile-username {
1037
+ font-size: 1.8rem;
1038
+ }
1039
+ .profile-email {
1040
+ font-size: 1rem;
1041
+ }
1042
+ .profile-section {
1043
+ padding: 1rem 1.2rem;
1044
+ }
1045
+ .profile-section-title {
1046
+ font-size: 1.4rem;
1047
+ }
1048
+ .profile-info-item {
1049
+ flex-direction: column;
1050
+ align-items: flex-start;
1051
+ gap: 0.2rem;
1052
+ }
1053
+ .profile-info-label {
1054
+ flex-basis: auto;
1055
+ font-size: 0.9rem;
1056
+ }
1057
+ .profile-info-value {
1058
+ flex-basis: auto;
1059
+ text-align: left;
1060
+ font-size: 0.95rem;
1061
+ }
1062
+ .profile-buttons-area {
1063
+ margin-top: 1.5rem;
1064
+ padding-top: 1.5rem;
1065
+ }
1066
+ .gakr-button-primary, .gakr-button-secondary {
1067
+ max-width: none; /* Full width on small screens */
1068
+ }
1069
+ .chat-history-sort-container {
1070
+ justify-content: flex-start; /* Align left on small screens */
1071
+ }
1072
+ .tag-options-grid {
1073
+ grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
1074
+ gap: 0.5rem;
1075
+ }
1076
+ .tag-option {
1077
+ padding: 0.4rem 0.6rem;
1078
+ font-size: 0.8rem;
1079
+ }
1080
+ }
1081
+
1082
+ @media (max-width: 480px) {
1083
+ .gakr-brand-text {
1084
+ font-size: 1.3rem;
1085
+ }
1086
+ .profile-avatar-large {
1087
+ width: 90px;
1088
+ height: 90px;
1089
+ font-size: 3rem;
1090
+ }
1091
+ .profile-username {
1092
+ font-size: 1.5rem;
1093
+ }
1094
+ .username-edit-input, .username-save-button {
1095
+ font-size: 0.9rem;
1096
+ padding: 0.5rem 0.8rem;
1097
+ }
1098
+ .about-me-edit-textarea, .about-me-save-button {
1099
+ font-size: 0.9rem;
1100
+ padding: 0.6rem 1rem;
1101
+ }
1102
+ .chat-history-item {
1103
+ padding: 0.8rem 1rem;
1104
+ }
1105
+ .chat-history-item .chat-title {
1106
+ font-size: 1rem;
1107
+ }
1108
+ .chat-history-item .chat-meta {
1109
+ font-size: 0.75rem;
1110
+ }
1111
+ .chat-history-sort-select {
1112
+ font-size: 0.85rem;
1113
+ padding: 0.2rem 0.5rem;
1114
+ }
1115
+ .gakr-modal-content {
1116
+ padding: 1.5rem;
1117
+ }
1118
+ .gakr-modal-title {
1119
+ font-size: 1.3rem;
1120
+ }
1121
+ .gakr-modal-message {
1122
+ font-size: 0.9rem;
1123
+ }
1124
+ .gakr-modal-buttons button {
1125
+ padding: 0.5rem 1rem;
1126
+ font-size: 0.9rem;
1127
+ }
1128
+ }
1129
+ </style>
1130
+ </head>
1131
+ <body class="on-profile-page">
1132
+ <div class="gakr-layout">
1133
+ <header class="gakr-chat-header">
1134
+ <div class="gakr-logo-area">
1135
+ <a href="/" class="gakr-brand-logo">
1136
+ <i class="fas fa-robot" style="color: var(--gakr-blue);"></i>
1137
+ </a>
1138
+ <a href="/" class="gakr-brand-text" style="text-decoration: none;">GAKR AI</a>
1139
+ </div>
1140
+
1141
+ <div class="gakr-nav-controls" id="authButtonContainer">
1142
+ <a href="/auth" class="gakr-login-button">Sign in / Register</a>
1143
+ </div>
1144
+ </header>
1145
+
1146
+ <div class="profile-view" id="profileView">
1147
+ <div class="profile-header-section">
1148
+ <div class="profile-avatar-large" id="profileAvatarLarge">
1149
+ U
1150
+ <label for="avatarUpload" class="profile-avatar-edit-icon" title="Change Avatar">
1151
+ <i class="fas fa-camera"></i>
1152
+ <input type="file" id="avatarUpload" accept="image/*" style="display: none;">
1153
+ </label>
1154
+ </div>
1155
+ <div class="profile-username-container">
1156
+ <h1 class="profile-username" id="profileUsernameDisplay">User Name</h1>
1157
+ <i class="fas fa-pencil-alt username-edit-icon" id="editUsernameIcon" title="Edit Username"></i>
1158
+ </div>
1159
+ <div class="username-input-container" id="usernameInputContainer">
1160
+ <input type="text" id="usernameEditInput" class="username-edit-input" placeholder="Enter new username">
1161
+ <button id="usernameSaveButton" class="username-save-button">Save</button>
1162
+ </div>
1163
+ <p class="profile-email" id="profileEmailDisplay">user.email@example.com</p>
1164
+ </div>
1165
+
1166
+ <div class="profile-section">
1167
+ <h2 class="profile-section-title">Account Information</h2>
1168
+ <div class="profile-info-item">
1169
+ <span class="profile-info-label">Name:</span>
1170
+ <span class="profile-info-value" id="accountName">User Name</span>
1171
+ </div>
1172
+ <div class="profile-info-item">
1173
+ <span class="profile-info-label">Email:</span>
1174
+ <span class="profile-info-value" id="accountEmail">user.email@example.com</span>
1175
+ </div>
1176
+ </div>
1177
+
1178
+ <div class="profile-section">
1179
+ <h2 class="profile-section-title">
1180
+ About Me
1181
+ <i class="fas fa-pencil-alt section-edit-icon about-me-edit-icon" id="editAboutMeIcon" title="Edit About Me"></i>
1182
+ </h2>
1183
+ <div class="profile-about-me-display" id="profileAboutMeDisplay">Tell us something about yourself...</div>
1184
+ <div class="about-me-input-container" id="aboutMeInputContainer">
1185
+ <textarea id="aboutMeEditInput" class="about-me-edit-textarea" placeholder="Write a short bio..."></textarea>
1186
+ <button id="aboutMeSaveButton" class="about-me-save-button">Save</button>
1187
+ </div>
1188
+ </div>
1189
+
1190
+ <div class="profile-section">
1191
+ <h2 class="profile-section-title">Recent Conversations</h2>
1192
+ <div class="chat-history-search-container">
1193
+ <input type="text" id="chatHistorySearchInput" class="chat-history-search-input" placeholder="Search conversations...">
1194
+ <i class="fas fa-search chat-history-search-icon"></i>
1195
+ </div>
1196
+ <div class="profile-chat-history" id="chatHistoryList">
1197
+ <p class="empty-chat-history" id="emptyChatHistoryMessage" style="display: none;">No conversations yet. Start chatting with GAKR AI!</p>
1198
+ </div>
1199
+ </div>
1200
+
1201
+ <div class="profile-buttons-area">
1202
+ <a href="/chat" class="gakr-button-primary" id="startChattingButton">
1203
+ <i class="fas fa-comments me-2"></i> Start Chatting with GAKR AI
1204
+ </a>
1205
+ <button
1206
+ type="button"
1207
+ class="gakr-button-primary"
1208
+ id="profileSettingsButton">
1209
+ <i class="fas fa-cog me-2"></i> Settings
1210
+ </button>
1211
+
1212
+
1213
+ <a href="#" class="gakr-button-secondary" id="profileLogoutButton" onclick="logoutFunction()">
1214
+ <i class="fas fa-sign-out-alt me-2"></i> Log out
1215
+ </a>
1216
+ </div>
1217
+ </div>
1218
+
1219
+ <footer class="gakr-footer">
1220
+ <div>GAKR AI - Powered by Advanced Local AI</div>
1221
+ </footer>
1222
+
1223
+ <div id="chatContextMenu" class="chat-context-menu">
1224
+ <a href="#" class="chat-context-menu-item" data-action="delete"><i class="fas fa-trash-alt"></i> Delete</a>
1225
+ <a href="#" class="chat-context-menu-item" data-action="share"><i class="fas fa-share-alt"></i> Share</a>
1226
+ <a href="#" class="chat-context-menu-item pin-unpin" data-action="pin"><i class="fas fa-thumbtack"></i> Pin</a>
1227
+ <a href="#" class="chat-context-menu-item" data-action="tag"><i class="fas fa-tag"></i> Add Tag</a>
1228
+ </div>
1229
+ </div>
1230
+
1231
+ <div class="toast-container" id="toastContainer"></div>
1232
+
1233
+ <div class="gakr-modal-overlay" id="confirmModal">
1234
+ <div class="gakr-modal-content">
1235
+ <h3 class="gakr-modal-title" id="confirmTitle">Confirm Action</h3>
1236
+ <p class="gakr-modal-message" id="confirmMessage">Are you sure you want to proceed?</p>
1237
+ <div class="gakr-modal-buttons">
1238
+ <button class="confirm-cancel" id="confirmCancelButton">Cancel</button>
1239
+ <button class="confirm-ok" id="confirmOkButton">Confirm</button>
1240
+ </div>
1241
+ </div>
1242
+ </div>
1243
+
1244
+ <div class="gakr-modal-overlay" id="promptModal">
1245
+ <div class="gakr-modal-content">
1246
+ <h3 class="gakr-modal-title" id="promptTitle">Enter Information</h3>
1247
+ <p class="gakr-modal-message" id="promptMessage">Please provide the required input:</p>
1248
+ <input type="text" class="gakr-modal-input" id="promptInput" placeholder="Enter text here...">
1249
+ <div class="gakr-modal-buttons">
1250
+ <button class="prompt-cancel" id="promptCancelButton">Cancel</button>
1251
+ <button class="prompt-submit" id="promptSubmitButton">Submit</button>
1252
+ </div>
1253
+ </div>
1254
+ </div>
1255
+
1256
+ <div class="gakr-modal-overlay" id="tagModal">
1257
+ <div class="tag-selection-modal-content">
1258
+ <h3 class="tag-selection-modal-title" id="tagModalTitle">Add Tag to Conversation</h3>
1259
+ <div class="tag-options-grid" id="tagOptions">
1260
+ </div>
1261
+ <div class="tag-actions">
1262
+ <button class="confirm-cancel" id="tagCancelButton">Cancel</button>
1263
+ <button class="confirm-ok" id="tagSaveButton">Save</button>
1264
+ </div>
1265
+ </div>
1266
+ </div>
1267
+
1268
+ <script>
1269
+ // Global modal elements
1270
+ let confirmModal, confirmTitle, confirmMessage, confirmOkButton, confirmCancelButton;
1271
+
1272
+ // Logout function - defined globally for onclick
1273
+ async function logoutFunction() {
1274
+ const confirmed = await showConfirmDialog('Are you sure you want to log out?', 'Logout Confirmation');
1275
+ if (confirmed) {
1276
+ localStorage.removeItem('isLoggedIn');
1277
+ sessionStorage.clear(); // Clear all session data
1278
+ window.location.href = '/'; // Redirect to home/login immediately
1279
+ }
1280
+ }
1281
+
1282
+ // Custom Confirm Dialog Function (Global)
1283
+ function showConfirmDialog(message, title = 'Confirm Action') {
1284
+ return new Promise((resolve) => {
1285
+ if (!confirmModal) {
1286
+ // Fallback if modal not loaded yet
1287
+ const confirmed = window.confirm(message);
1288
+ resolve(confirmed);
1289
+ return;
1290
+ }
1291
+
1292
+ confirmTitle.textContent = title;
1293
+ confirmMessage.textContent = message;
1294
+ confirmModal.classList.add('show');
1295
+
1296
+ // Store the element that triggered the modal for focus management
1297
+ const triggeringElement = document.activeElement;
1298
+
1299
+ const handleOk = () => {
1300
+ confirmModal.classList.remove('show');
1301
+ confirmOkButton.removeEventListener('click', handleOk);
1302
+ confirmCancelButton.removeEventListener('click', handleCancel);
1303
+ if (triggeringElement) triggeringElement.focus(); // Return focus
1304
+ resolve(true);
1305
+ };
1306
+
1307
+ const handleCancel = () => {
1308
+ confirmModal.classList.remove('show');
1309
+ confirmOkButton.removeEventListener('click', handleOk);
1310
+ confirmCancelButton.removeEventListener('click', handleCancel);
1311
+ if (triggeringElement) triggeringElement.focus(); // Return focus
1312
+ resolve(false);
1313
+ };
1314
+
1315
+ confirmOkButton.addEventListener('click', handleOk);
1316
+ confirmCancelButton.addEventListener('click', handleCancel);
1317
+
1318
+ // Close on overlay click (but prevent immediate closing if clicking content)
1319
+ confirmModal.addEventListener('click', function(event) {
1320
+ if (event.target === confirmModal) { // Only close if click is directly on the overlay
1321
+ handleCancel();
1322
+ }
1323
+ }, { once: true }); // Ensure it only runs once per dialog instance
1324
+ });
1325
+ }
1326
+
1327
+ document.addEventListener('DOMContentLoaded', function() {
1328
+ // --- Authentication Status Check ---
1329
+ function updateAuthButton() {
1330
+ const authButtonContainer = document.getElementById('authButtonContainer');
1331
+ const userIsLoggedIn = localStorage.getItem('isLoggedIn') === 'true';
1332
+
1333
+ if (userIsLoggedIn) {
1334
+ authButtonContainer.innerHTML = `
1335
+ <a href="/profile" class="gakr-profile-button">
1336
+ <i class="fas fa-user-circle me-2"></i>Profile
1337
+ </a>
1338
+ `;
1339
+ const profileButton = authButtonContainer.querySelector('.gakr-profile-button');
1340
+ if (profileButton) {
1341
+ profileButton.addEventListener('click', function(event) {
1342
+ console.log("Profile button clicked!");
1343
+ });
1344
+ }
1345
+ } else {
1346
+ authButtonContainer.innerHTML = `
1347
+ <a href="/auth" class="gakr-login-button">Sign in / Register</a>
1348
+ `;
1349
+ }
1350
+ }
1351
+
1352
+ updateAuthButton();
1353
+ window.addEventListener('focus', updateAuthButton);
1354
+ window.addEventListener('storage', updateAuthButton);
1355
+
1356
+ // Check if user is logged in
1357
+ if (localStorage.getItem('isLoggedIn') !== 'true') {
1358
+ window.location.href = '/auth';
1359
+ return;
1360
+ }
1361
+ // --- DOM Element References ---
1362
+ const profileButton = document.getElementById('profileButton');
1363
+ const profileDropdown = document.getElementById('profileDropdown');
1364
+ const logoutButton = document.getElementById('logoutButton');
1365
+ const brandLogoArea = document.getElementById('brandLogoArea');
1366
+ const signInRegisterButton = document.getElementById('signInRegisterButton');
1367
+ const profileControls = document.getElementById('profileControls');
1368
+
1369
+ // Profile-specific elements
1370
+ const profileAvatarLarge = document.getElementById('profileAvatarLarge');
1371
+ const profileUsernameDisplay = document.getElementById('profileUsernameDisplay');
1372
+ const profileEmailDisplay = document.getElementById('profileEmailDisplay');
1373
+ const accountName = document.getElementById('accountName');
1374
+ const accountEmail = document.getElementById('accountEmail');
1375
+ const chatHistoryList = document.getElementById('chatHistoryList');
1376
+ const profileSettingsButton = document.getElementById('profileSettingsButton');
1377
+ const profileLogoutButton = document.getElementById('profileLogoutButton');
1378
+ const profileLink = document.getElementById('profileLink');
1379
+ const settingsLink = document.getElementById('settingsLink');
1380
+ const emptyChatHistoryMessage = document.getElementById('emptyChatHistoryMessage');
1381
+
1382
+ // Check login
1383
+ if (localStorage.getItem('isLoggedIn') !== 'true') {
1384
+ window.location.href = '/auth';
1385
+ }
1386
+
1387
+ // Load user data
1388
+ const user = JSON.parse(localStorage.getItem('user') || '{}');
1389
+ if (user.name) {
1390
+ profileUsernameDisplay.textContent = user.name;
1391
+ accountName.textContent = user.name;
1392
+ }
1393
+ if (user.email) {
1394
+ profileEmailDisplay.textContent = user.email;
1395
+ accountEmail.textContent = user.email;
1396
+ }
1397
+
1398
+ // Elements for edit functionality
1399
+ // Note: avatarUpload needs to be re-referenced after dynamic re-creation
1400
+ let avatarUpload = document.getElementById('avatarUpload');
1401
+ const editUsernameIcon = document.getElementById('editUsernameIcon');
1402
+ const usernameInputContainer = document.getElementById('usernameInputContainer');
1403
+ const usernameEditInput = document.getElementById('usernameEditInput');
1404
+ const usernameSaveButton = document.getElementById('usernameSaveButton');
1405
+
1406
+ // About Me Section elements
1407
+ const profileAboutMeDisplay = document.getElementById('profileAboutMeDisplay');
1408
+ const editAboutMeIcon = document.getElementById('editAboutMeIcon');
1409
+ const aboutMeInputContainer = document.getElementById('aboutMeInputContainer');
1410
+ const aboutMeEditInput = document.getElementById('aboutMeEditInput');
1411
+ const aboutMeSaveButton = document.getElementById('aboutMeSaveButton');
1412
+
1413
+ // Chat History Search
1414
+ const chatHistorySearchInput = document.getElementById('chatHistorySearchInput');
1415
+
1416
+ // Chat Context Menu elements
1417
+ const chatContextMenu = document.getElementById('chatContextMenu');
1418
+ let currentChatContextItem = null; // To keep track of which chat item was right-clicked
1419
+
1420
+ // Toast Container
1421
+ const toastContainer = document.getElementById('toastContainer');
1422
+
1423
+ // Custom Modal Elements
1424
+ confirmModal = document.getElementById('confirmModal');
1425
+ confirmTitle = document.getElementById('confirmTitle');
1426
+ confirmMessage = document.getElementById('confirmMessage');
1427
+ confirmOkButton = document.getElementById('confirmOkButton');
1428
+ confirmCancelButton = document.getElementById('confirmCancelButton');
1429
+
1430
+ const promptModal = document.getElementById('promptModal');
1431
+ const promptTitle = document.getElementById('promptTitle');
1432
+ const promptMessage = document.getElementById('promptMessage');
1433
+ const promptInput = document.getElementById('promptInput');
1434
+ const promptSubmitButton = document.getElementById('promptSubmitButton');
1435
+ const promptCancelButton = document.getElementById('promptCancelButton');
1436
+
1437
+ // Tag Modal Elements (New)
1438
+ const tagModal = document.getElementById('tagModal');
1439
+ const tagModalTitle = document.getElementById('tagModalTitle');
1440
+ const tagOptions = document.getElementById('tagOptions');
1441
+ const tagSaveButton = document.getElementById('tagSaveButton');
1442
+ const tagCancelButton = document.getElementById('tagCancelButton');
1443
+ let currentChatIdForTagging = null;
1444
+ let selectedTags = new Set(); // Stores tags currently selected in the modal
1445
+
1446
+ // New button
1447
+ const startChattingButton = document.getElementById('startChattingButton');
1448
+
1449
+
1450
+ // --- Mock Data (Enhanced) ---
1451
+ // Data is stored in sessionStorage to persist across page reloads in a session
1452
+ let mockUserData = JSON.parse(sessionStorage.getItem('userData')) || {
1453
+ name: 'GAKR User',
1454
+ email: 'gakr.user@example.com',
1455
+ avatar: null, // Base64 string for avatar image
1456
+ aboutMe: '',
1457
+ pinnedChats: [] // Stored as array, converted to Set on load/save
1458
+ };
1459
+
1460
+ // Ensure pinnedChats is a Set for quick lookups and consistency
1461
+ if (mockUserData.pinnedChats && Array.isArray(mockUserData.pinnedChats)) {
1462
+ mockUserData.pinnedChats = new Set(mockUserData.pinnedChats);
1463
+ } else if (!mockUserData.pinnedChats) {
1464
+ mockUserData.pinnedChats = new Set();
1465
+ }
1466
+
1467
+ let mockChatHistory = JSON.parse(sessionStorage.getItem('mockChatHistory')) || [
1468
+ { id: 1, title: "What is AI?", date: "2 days ago", tags: ['Learning'], lastActive: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString() },
1469
+ { id: 2, title: "Poem about the ocean", date: "Yesterday", tags: ['Personal', 'Creative'], lastActive: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000).toISOString() },
1470
+ { id: 3, title: "Explain quantum physics to me simply", date: "Today", tags: ['Learning', 'Ideas'], lastActive: new Date().toISOString() },
1471
+ { id: 4, title: "Recipe for a simple pasta dish", date: "3 days ago", tags: ['Personal'], lastActive: new Date(Date.now() - 3 * 24 * 60 * 60 * 1000).toISOString() },
1472
+ { id: 5, title: "History of the internet", date: "Last week", tags: ['Learning', 'Work'], lastActive: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString() }
1473
+ ];
1474
+
1475
+ // Convert tags arrays to Sets when loading, for consistency with mockUserData.pinnedChats
1476
+ mockChatHistory = mockChatHistory.map(chat => ({
1477
+ ...chat,
1478
+ tags: new Set(chat.tags || [])
1479
+ }));
1480
+
1481
+
1482
+ // --- Utility Functions ---
1483
+
1484
+ // Animated Toast Message Function (Middle of Page)
1485
+ function showToast(message, type = 'info', duration = 3000) {
1486
+ const toast = document.createElement('div');
1487
+ toast.className = `gakr-toast ${type}`;
1488
+ let iconClass = '';
1489
+ switch(type) {
1490
+ case 'success': iconClass = 'fas fa-check-circle'; break;
1491
+ case 'error': iconClass = 'fas fa-times-circle'; break;
1492
+ case 'warning': iconClass = 'fas fa-exclamation-triangle'; break;
1493
+ default: iconClass = 'fas fa-info-circle'; break;
1494
+ }
1495
+ toast.innerHTML = `<i class="${iconClass} toast-icon"></i> <span>${message}</span>`;
1496
+ toastContainer.appendChild(toast);
1497
+
1498
+ // Show animation
1499
+ setTimeout(() => {
1500
+ toast.classList.add('show');
1501
+ }, 10); // Small delay for CSS transition to work
1502
+
1503
+ // Hide animation and remove after duration
1504
+ setTimeout(() => {
1505
+ toast.classList.remove('show');
1506
+ toast.classList.add('hide'); // Add hide class for exit animation
1507
+ toast.addEventListener('transitionend', () => toast.remove(), { once: true });
1508
+ }, duration);
1509
+ }
1510
+
1511
+ // Custom Prompt Dialog Function (Middle of Page)
1512
+ function showPromptDialog(message, defaultValue = '', title = 'Enter Information') {
1513
+ return new Promise((resolve) => {
1514
+ promptTitle.textContent = title;
1515
+ promptMessage.textContent = message;
1516
+ promptInput.value = defaultValue;
1517
+ promptModal.classList.add('show');
1518
+ promptInput.focus(); // Focus on the input field
1519
+
1520
+ const triggeringElement = document.activeElement;
1521
+
1522
+ const handleSubmit = () => {
1523
+ promptModal.classList.remove('show');
1524
+ promptSubmitButton.removeEventListener('click', handleSubmit);
1525
+ promptCancelButton.removeEventListener('click', handleCancel);
1526
+ promptInput.removeEventListener('keypress', handleKeyPress);
1527
+ if (triggeringElement) triggeringElement.focus(); // Return focus
1528
+ resolve(promptInput.value);
1529
+ };
1530
+
1531
+ const handleCancel = () => {
1532
+ promptModal.classList.remove('show');
1533
+ promptSubmitButton.removeEventListener('click', handleSubmit);
1534
+ promptCancelButton.removeEventListener('click', handleCancel);
1535
+ promptInput.removeEventListener('keypress', handleKeyPress);
1536
+ if (triggeringElement) triggeringElement.focus(); // Return focus
1537
+ resolve(null); // Return null if cancelled
1538
+ };
1539
+
1540
+ const handleKeyPress = (event) => {
1541
+ if (event.key === 'Enter') {
1542
+ handleSubmit();
1543
+ }
1544
+ };
1545
+
1546
+ promptSubmitButton.addEventListener('click', handleSubmit);
1547
+ promptCancelButton.addEventListener('click', handleCancel);
1548
+ promptInput.addEventListener('keypress', handleKeyPress);
1549
+
1550
+ // Close on overlay click
1551
+ promptModal.addEventListener('click', function(event) {
1552
+ if (event.target === promptModal) {
1553
+ handleCancel();
1554
+ }
1555
+ }, { once: true });
1556
+ });
1557
+ }
1558
+
1559
+ // --- User Data Management (Mock) ---
1560
+ function getUserData() {
1561
+ return mockUserData;
1562
+ }
1563
+
1564
+ function setUserData(data) {
1565
+ // Merge new data while preserving existing keys
1566
+ mockUserData = { ...mockUserData, ...data };
1567
+ // Ensure pinnedChats is always an Array when saving to sessionStorage for JSON stringify
1568
+ const dataToSave = {
1569
+ ...mockUserData,
1570
+ pinnedChats: Array.from(mockUserData.pinnedChats)
1571
+ };
1572
+ sessionStorage.setItem('userData', JSON.stringify(dataToSave));
1573
+ updateProfileUI(); // Update UI after data change
1574
+ }
1575
+
1576
+ function getChatHistory() {
1577
+ // Return a deep copy to prevent direct modification outside of updateChatInHistory
1578
+ return JSON.parse(JSON.stringify(Array.from(mockChatHistory).map(chat => ({ ...chat, tags: Array.from(chat.tags) }))));
1579
+ }
1580
+
1581
+ function saveChatHistory() {
1582
+ // Convert tags back to arrays for storage
1583
+ const chatsToSave = mockChatHistory.map(chat => ({
1584
+ ...chat,
1585
+ tags: Array.from(chat.tags) // Convert Set to Array for JSON stringify
1586
+ }));
1587
+ sessionStorage.setItem('mockChatHistory', JSON.stringify(chatsToSave));
1588
+ }
1589
+
1590
+ function updateChatInHistory(chatId, update) {
1591
+ const index = mockChatHistory.findIndex(chat => chat.id === chatId);
1592
+ if (index !== -1) {
1593
+ // Ensure tags are handled as Sets internally
1594
+ if (update.tags && ! (update.tags instanceof Set)) {
1595
+ update.tags = new Set(update.tags);
1596
+ }
1597
+ mockChatHistory[index] = { ...mockChatHistory[index], ...update };
1598
+ saveChatHistory();
1599
+ }
1600
+ }
1601
+
1602
+ function deleteChatFromHistory(chatId) {
1603
+ const initialLength = mockChatHistory.length;
1604
+ mockChatHistory = mockChatHistory.filter(chat => chat.id !== chatId);
1605
+ if (mockUserData.pinnedChats.has(chatId)) {
1606
+ mockUserData.pinnedChats.delete(chatId);
1607
+ setUserData({}); // Save updated pinned chats (which triggers UI update)
1608
+ } else {
1609
+ saveChatHistory(); // Only save if pinned chats weren't updated
1610
+ }
1611
+ return mockChatHistory.length < initialLength; // Return true if deleted
1612
+ }
1613
+
1614
+ // --- UI Update Logic ---
1615
+ function updateProfileUI() {
1616
+ const userData = getUserData();
1617
+
1618
+ // Update header profile button
1619
+ if (profileButton) {
1620
+ profileButton.textContent = userData.name.charAt(0).toUpperCase();
1621
+ profileButton.title = userData.name;
1622
+ }
1623
+
1624
+ // Update large avatar
1625
+ if (profileAvatarLarge) {
1626
+ profileAvatarLarge.innerHTML = ''; // Clear existing content
1627
+ if (userData.avatar) {
1628
+ const img = document.createElement('img');
1629
+ img.src = userData.avatar;
1630
+ img.alt = "User Avatar";
1631
+ profileAvatarLarge.appendChild(img);
1632
+ } else {
1633
+ profileAvatarLarge.textContent = userData.name.charAt(0).toUpperCase();
1634
+ }
1635
+ // Add the edit icon and input back dynamically
1636
+ const label = document.createElement('label');
1637
+ label.htmlFor = 'avatarUpload'; // Important: ensures label clicks input
1638
+ label.className = 'profile-avatar-edit-icon';
1639
+ label.title = 'Change Avatar';
1640
+ label.innerHTML = '<i class="fas fa-camera"></i>';
1641
+ // Create a *new* input element and assign its ID, then append to label
1642
+ const newInput = document.createElement('input');
1643
+ newInput.type = 'file';
1644
+ newInput.id = 'avatarUpload'; // Reuse the ID
1645
+ newInput.accept = 'image/*';
1646
+ newInput.style.display = 'none';
1647
+ label.appendChild(newInput);
1648
+ profileAvatarLarge.appendChild(label);
1649
+
1650
+ // Re-assign the global avatarUpload reference to the new element
1651
+ avatarUpload = newInput;
1652
+ // Re-attach event listener for the dynamically created input
1653
+ avatarUpload.addEventListener('change', handleAvatarUpload);
1654
+ }
1655
+
1656
+ // Update username displays
1657
+ if (profileUsernameDisplay) profileUsernameDisplay.textContent = userData.name;
1658
+ if (accountName) accountName.textContent = userData.name;
1659
+ if (profileEmailDisplay) profileEmailDisplay.textContent = userData.email;
1660
+ if (accountEmail) accountEmail.textContent = userData.email;
1661
+
1662
+ // Update About Me display
1663
+ if (profileAboutMeDisplay) {
1664
+ profileAboutMeDisplay.textContent = userData.aboutMe || 'Tell us something about yourself...';
1665
+ // Add/remove placeholder class for italicized gray text
1666
+ profileAboutMeDisplay.classList.toggle('placeholder', !userData.aboutMe);
1667
+ }
1668
+
1669
+
1670
+ // Ensure auth controls are set for a "logged in" state
1671
+ if (signInRegisterButton) signInRegisterButton.style.display = 'none';
1672
+ if (profileControls) profileControls.style.display = 'block';
1673
+
1674
+ // Update chat history list based on current filters
1675
+ renderChatHistory(chatHistorySearchInput.value);
1676
+ }
1677
+
1678
+ // --- Edit Profile Functions ---
1679
+ function handleAvatarUpload(event) {
1680
+ const file = event.target.files[0];
1681
+ if (file) {
1682
+ const reader = new FileReader();
1683
+ reader.onload = function(e) {
1684
+ setUserData({ avatar: e.target.result });
1685
+ showToast('Avatar updated successfully!', 'success');
1686
+ };
1687
+ reader.onerror = function() {
1688
+ showToast('Failed to read file.', 'error');
1689
+ };
1690
+ reader.readAsDataURL(file);
1691
+ } else {
1692
+ showToast('No file selected.', 'warning');
1693
+ }
1694
+ }
1695
+
1696
+ function toggleUsernameEdit(show) {
1697
+ if (show) {
1698
+ profileUsernameDisplay.style.display = 'none';
1699
+ editUsernameIcon.style.display = 'none';
1700
+ usernameInputContainer.style.display = 'flex'; // Use flex for button alignment
1701
+ usernameEditInput.value = getUserData().name;
1702
+ usernameEditInput.focus();
1703
+ usernameEditInput.setSelectionRange(usernameEditInput.value.length, usernameEditInput.value.length); // Put cursor at end
1704
+ } else {
1705
+ profileUsernameDisplay.style.display = 'block';
1706
+ editUsernameIcon.style.display = 'inline-block';
1707
+ usernameInputContainer.style.display = 'none';
1708
+ }
1709
+ }
1710
+
1711
+ function toggleAboutMeEdit(show) {
1712
+ if (show) {
1713
+ profileAboutMeDisplay.style.display = 'none';
1714
+ editAboutMeIcon.style.display = 'none';
1715
+ aboutMeInputContainer.style.display = 'flex'; // Use flex-direction: column in CSS
1716
+ aboutMeEditInput.value = getUserData().aboutMe;
1717
+ aboutMeEditInput.focus();
1718
+ aboutMeEditInput.setSelectionRange(aboutMeEditInput.value.length, aboutMeEditInput.value.length); // Put cursor at end
1719
+ } else {
1720
+ profileAboutMeDisplay.style.display = 'block';
1721
+ editAboutMeIcon.style.display = 'inline-block';
1722
+ aboutMeInputContainer.style.display = 'none';
1723
+ }
1724
+ }
1725
+
1726
+ // --- Chat History Rendering and Management ---
1727
+
1728
+ function renderChatHistory(searchTerm = '') {
1729
+ const chatHistory = getChatHistory();
1730
+ chatHistoryList.innerHTML = ''; // Clear existing list
1731
+
1732
+ // Filter
1733
+ let filteredChats = chatHistory.filter(chat =>
1734
+ chat.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
1735
+ Array.from(chat.tags).some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase()))
1736
+ );
1737
+
1738
+ // Separate pinned and unpinned chats
1739
+ let pinnedChats = [];
1740
+ let unpinnedChats = [];
1741
+
1742
+ filteredChats.forEach(chat => {
1743
+ // Check against mockUserData.pinnedChats Set
1744
+ if (mockUserData.pinnedChats.has(chat.id)) {
1745
+ pinnedChats.push(chat);
1746
+ } else {
1747
+ unpinnedChats.push(chat);
1748
+ }
1749
+ });
1750
+
1751
+ // Sort (always newest first for simplicity after removing sort dropdown)
1752
+ const sortFunction = (a, b) => {
1753
+ const dateA = new Date(a.lastActive);
1754
+ const dateB = new Date(b.lastActive);
1755
+ return dateB.getTime() - dateA.getTime(); // Newest first
1756
+ };
1757
+
1758
+ pinnedChats.sort(sortFunction);
1759
+ unpinnedChats.sort(sortFunction);
1760
+
1761
+ // Combine pinned and unpinned (pinned always come first)
1762
+ const chatsToDisplay = [...pinnedChats, ...unpinnedChats];
1763
+
1764
+ if (chatsToDisplay.length === 0) {
1765
+ emptyChatHistoryMessage.style.display = 'block';
1766
+ } else {
1767
+ emptyChatHistoryMessage.style.display = 'none';
1768
+ chatsToDisplay.forEach(chat => {
1769
+ const chatItem = document.createElement('div');
1770
+ chatItem.className = 'chat-history-item';
1771
+ chatItem.dataset.chatId = chat.id; // Store chat ID on the element
1772
+ if (mockUserData.pinnedChats.has(chat.id)) {
1773
+ chatItem.classList.add('pinned');
1774
+ }
1775
+
1776
+ // Format last active time
1777
+ let lastActiveText = '';
1778
+ try {
1779
+ const lastActiveDate = new Date(chat.lastActive);
1780
+ const now = new Date();
1781
+ const diffMs = now.getTime() - lastActiveDate.getTime();
1782
+ const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
1783
+
1784
+ if (diffDays === 0 && lastActiveDate.toDateString() === now.toDateString()) {
1785
+ lastActiveText = `Today, ${lastActiveDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`;
1786
+ } else if (diffDays === 1 || (diffDays === 0 && lastActiveDate.getDate() === now.getDate() - 1)) {
1787
+ lastActiveText = `Yesterday, ${lastActiveDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`;
1788
+ } else if (diffDays < 7) {
1789
+ lastActiveText = `${lastActiveDate.toLocaleDateString('en-US', { weekday: 'short' })}, ${lastActiveDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`;
1790
+ } else {
1791
+ lastActiveText = lastActiveDate.toLocaleDateString();
1792
+ }
1793
+ } catch (e) {
1794
+ lastActiveText = chat.date; // Fallback if date is invalid or missing
1795
+ }
1796
+
1797
+
1798
+ chatItem.innerHTML = `
1799
+ <div class="chat-title-wrapper">
1800
+ <span class="chat-title">${chat.title}</span>
1801
+ ${mockUserData.pinnedChats.has(chat.id) ? '<i class="fas fa-thumbtack pinned-icon" title="Pinned"></i>' : ''}
1802
+ </div>
1803
+ <div class="chat-meta">
1804
+ <span class="chat-date">${chat.date}</span>
1805
+ <span class="chat-history-last-active">Last active: ${lastActiveText}</span>
1806
+ </div>
1807
+ <div class="chat-tags">
1808
+ ${Array.from(chat.tags).map(tag => `<span>${tag}</span>`).join('')}
1809
+ </div>
1810
+ `;
1811
+ chatHistoryList.appendChild(chatItem);
1812
+ });
1813
+ }
1814
+ }
1815
+
1816
+ // --- Context Menu Functions ---
1817
+ function showContextMenu(x, y, chatItem) {
1818
+ // Ensure menu doesn't go off-screen
1819
+ let posX = x;
1820
+ let posY = y;
1821
+
1822
+ // Adjust X if too close to right edge
1823
+ const menuWidth = chatContextMenu.offsetWidth;
1824
+ if (posX + menuWidth > window.innerWidth - 20) { // 20px padding from right
1825
+ posX = window.innerWidth - menuWidth - 20;
1826
+ }
1827
+ // Adjust Y if too close to bottom edge
1828
+ const menuHeight = chatContextMenu.offsetHeight;
1829
+ if (posY + menuHeight > window.innerHeight - 20) { // 20px padding from bottom
1830
+ posY = window.innerHeight - menuHeight - 20;
1831
+ }
1832
+
1833
+ chatContextMenu.style.left = `${posX}px`;
1834
+ chatContextMenu.style.top = `${posY}px`;
1835
+ chatContextMenu.classList.add('show');
1836
+ currentChatContextItem = chatItem; // Store the clicked item
1837
+
1838
+ const chatId = parseInt(currentChatContextItem.dataset.chatId);
1839
+ const pinUnpinLink = chatContextMenu.querySelector('[data-action="pin"], [data-action="unpin"]');
1840
+ if (mockUserData.pinnedChats.has(chatId)) {
1841
+ pinUnpinLink.innerHTML = '<i class="fas fa-thumbtack"></i> Unpin';
1842
+ pinUnpinLink.dataset.action = 'unpin';
1843
+ } else {
1844
+ pinUnpinLink.innerHTML = '<i class="fas fa-thumbtack"></i> Pin';
1845
+ pinUnpinLink.dataset.action = 'pin';
1846
+ }
1847
+ }
1848
+
1849
+ function hideContextMenu() {
1850
+ chatContextMenu.classList.remove('show');
1851
+ currentChatContextItem = null;
1852
+ }
1853
+
1854
+ // --- Tag Modal Functions (New) ---
1855
+ function showTagModal(chatId, currentTags) {
1856
+ currentChatIdForTagging = chatId;
1857
+ selectedTags = new Set(currentTags); // Initialize selected tags from the chat's current tags
1858
+
1859
+ // Clear previous selections and populate options
1860
+ tagOptions.innerHTML = '';
1861
+ // Extendable list of available tags
1862
+ const availableTags = ['Work', 'Personal', 'Creative', 'Ideas', 'Learning', 'Research', 'Drafts', 'Notes', 'Projects', 'Finance', 'Health'];
1863
+
1864
+ availableTags.forEach(tag => {
1865
+ const tagDiv = document.createElement('div');
1866
+ tagDiv.className = 'tag-option';
1867
+ tagDiv.dataset.tag = tag;
1868
+ tagDiv.textContent = tag;
1869
+ if (selectedTags.has(tag)) {
1870
+ tagDiv.classList.add('selected');
1871
+ }
1872
+ tagDiv.addEventListener('click', () => {
1873
+ if (selectedTags.has(tag)) {
1874
+ selectedTags.delete(tag);
1875
+ tagDiv.classList.remove('selected');
1876
+ } else {
1877
+ selectedTags.add(tag);
1878
+ tagDiv.classList.add('selected');
1879
+ }
1880
+ });
1881
+ tagOptions.appendChild(tagDiv);
1882
+ });
1883
+
1884
+ tagModal.classList.add('show');
1885
+ // Store the element that triggered the modal for focus management
1886
+ tagModal.triggeringElement = document.activeElement;
1887
+ }
1888
+
1889
+ function hideTagModal() {
1890
+ tagModal.classList.remove('show');
1891
+ currentChatIdForTagging = null;
1892
+ selectedTags = new Set(); // Reset selected tags
1893
+ if (tagModal.triggeringElement) tagModal.triggeringElement.focus(); // Return focus
1894
+ tagModal.triggeringElement = null;
1895
+ }
1896
+
1897
+
1898
+ // --- Event Listeners ---
1899
+
1900
+ // Dropdown menu toggle
1901
+ profileButton.addEventListener('click', function(event) {
1902
+ event.stopPropagation(); // Prevent document click from closing immediately
1903
+ profileDropdown.classList.toggle('show');
1904
+ });
1905
+
1906
+ // Close dropdown and context menu if clicked outside
1907
+ document.addEventListener('click', function(event) {
1908
+ if (!profileDropdown.contains(event.target) && !profileButton.contains(event.target)) {
1909
+ profileDropdown.classList.remove('show');
1910
+ }
1911
+ if (!chatContextMenu.contains(event.target) && !event.target.closest('.chat-history-item')) {
1912
+ hideContextMenu();
1913
+ }
1914
+ });
1915
+
1916
+ // Prevent context menu from closing if right-clicking the menu itself
1917
+ chatContextMenu.addEventListener('contextmenu', function(event) {
1918
+ event.preventDefault();
1919
+ });
1920
+
1921
+ // Navigation from dropdown (Mock functionality)
1922
+ if (profileLink) {
1923
+ profileLink.addEventListener('click', (e) => {
1924
+ e.preventDefault();
1925
+ profileDropdown.classList.remove('show');
1926
+ showToast('You are already on your profile page!', 'info');
1927
+ });
1928
+ }
1929
+ if (profileSettingsButton) {
1930
+ profileSettingsButton.addEventListener('click', function(e) {
1931
+ e.preventDefault();
1932
+ showToast('Profile settings coming soon!', 'info');
1933
+ });
1934
+ }
1935
+ if (settingsLink) {
1936
+ settingsLink.addEventListener('click', (e) => {
1937
+ e.preventDefault();
1938
+ showToast('Settings page is under development!', 'info');
1939
+ profileDropdown.classList.remove('show');
1940
+ });
1941
+ }
1942
+
1943
+ // Logout (from header dropdown) - removed, using onclick for profile logout
1944
+
1945
+ // Handle profile settings button on the profile page
1946
+
1947
+
1948
+ // Profile logout is now handled by onclick in HTML
1949
+
1950
+ // Username Edit Listeners
1951
+ editUsernameIcon.addEventListener('click', () => toggleUsernameEdit(true));
1952
+ usernameSaveButton.addEventListener('click', function() {
1953
+ const newUsername = usernameEditInput.value.trim();
1954
+ if (newUsername) {
1955
+ setUserData({ name: newUsername });
1956
+ toggleUsernameEdit(false);
1957
+ showToast('Username updated successfully!', 'success');
1958
+ } else {
1959
+ showToast('Username cannot be empty!', 'error');
1960
+ }
1961
+ });
1962
+ usernameEditInput.addEventListener('keypress', function(e) {
1963
+ if (e.key === 'Enter') {
1964
+ usernameSaveButton.click(); // Trigger save button click on Enter
1965
+ }
1966
+ });
1967
+
1968
+ // About Me Edit Listeners
1969
+ editAboutMeIcon.addEventListener('click', () => toggleAboutMeEdit(true));
1970
+ aboutMeSaveButton.addEventListener('click', function() {
1971
+ const newAboutMe = aboutMeEditInput.value.trim();
1972
+ setUserData({ aboutMe: newAboutMe }); // Allow empty string for about me
1973
+ toggleAboutMeEdit(false);
1974
+ showToast('About Me section updated!', 'success');
1975
+ });
1976
+ aboutMeEditInput.addEventListener('keypress', function(e) {
1977
+ if (e.key === 'Enter' && !e.shiftKey) { // Allow Shift+Enter for new line
1978
+ e.preventDefault(); // Prevent default Enter behavior (new line)
1979
+ aboutMeSaveButton.click();
1980
+ }
1981
+ });
1982
+
1983
+
1984
+ // Chat History Search Listener
1985
+ chatHistorySearchInput.addEventListener('input', () => renderChatHistory(chatHistorySearchInput.value));
1986
+
1987
+
1988
+ // Chat History Context Menu Listeners (Right-click)
1989
+ chatHistoryList.addEventListener('contextmenu', function(event) {
1990
+ const chatItem = event.target.closest('.chat-history-item');
1991
+ if (chatItem) {
1992
+ event.preventDefault(); // Prevent default browser context menu
1993
+ showContextMenu(event.clientX, event.clientY, chatItem);
1994
+ }
1995
+ });
1996
+
1997
+ // Handle actions from the chat context menu
1998
+ chatContextMenu.addEventListener('click', function(event) {
1999
+ event.preventDefault(); // Prevent default link behavior
2000
+ const actionElement = event.target.closest('.chat-context-menu-item');
2001
+ if (actionElement && currentChatContextItem) {
2002
+ const action = actionElement.dataset.action;
2003
+ const chatId = parseInt(currentChatContextItem.dataset.chatId);
2004
+
2005
+ switch (action) {
2006
+ case 'delete':
2007
+ showConfirmDialog('Are you sure you want to delete this conversation? This action cannot be undone.', 'Delete Conversation')
2008
+ .then(result => {
2009
+ if (result) {
2010
+ if (deleteChatFromHistory(chatId)) {
2011
+ showToast('Conversation deleted.', 'success');
2012
+ renderChatHistory(chatHistorySearchInput.value); // Re-render after deletion
2013
+ } else {
2014
+ showToast('Failed to delete conversation.', 'error');
2015
+ }
2016
+ }
2017
+ });
2018
+ break;
2019
+ case 'share':
2020
+ showPromptDialog('Enter email or username to share with:', '', 'Share Conversation')
2021
+ .then(input => {
2022
+ if (input !== null) { // Check for null (cancelled)
2023
+ if (input.trim() !== '') {
2024
+ showToast(`Conversation shared with "${input}". (Mock)`, 'success');
2025
+ } else {
2026
+ showToast('Share cancelled. No recipient provided.', 'warning');
2027
+ }
2028
+ }
2029
+ });
2030
+ break;
2031
+ case 'pin':
2032
+ mockUserData.pinnedChats.add(chatId);
2033
+ setUserData({}); // Save updated pinned chats (triggers UI update)
2034
+ showToast('Conversation pinned!', 'success');
2035
+ // renderChatHistory is called by setUserData
2036
+ break;
2037
+ case 'unpin':
2038
+ mockUserData.pinnedChats.delete(chatId);
2039
+ setUserData({}); // Save updated pinned chats (triggers UI update)
2040
+ showToast('Conversation unpinned!', 'info');
2041
+ // renderChatHistory is called by setUserData
2042
+ break;
2043
+ case 'tag':
2044
+ const chatToTag = mockChatHistory.find(chat => chat.id === chatId);
2045
+ if (chatToTag) {
2046
+ showTagModal(chatId, chatToTag.tags);
2047
+ }
2048
+ break;
2049
+ }
2050
+ hideContextMenu(); // Always hide after an action
2051
+ }
2052
+ });
2053
+
2054
+ // Handle clicks on chat items (to potentially open a conversation)
2055
+ chatHistoryList.addEventListener('click', function(event) {
2056
+ const chatItem = event.target.closest('.chat-history-item');
2057
+ if (chatItem) {
2058
+ const chatId = chatItem.dataset.chatId;
2059
+ showToast(`Opening conversation: ${chatItem.querySelector('.chat-title').textContent} (ID: ${chatId}). (Mock)`, 'info');
2060
+ // In a real application, you would navigate to the chat page:
2061
+ // window.location.href = `chat.html?chatId=${chatId}`;
2062
+ }
2063
+ });
2064
+
2065
+ // Tag Modal Listeners (New)
2066
+ tagSaveButton.addEventListener('click', function() {
2067
+ if (currentChatIdForTagging !== null) {
2068
+ // Update the chat with the new set of tags
2069
+ updateChatInHistory(currentChatIdForTagging, { tags: Array.from(selectedTags) });
2070
+ showToast('Tags updated successfully!', 'success');
2071
+ hideTagModal();
2072
+ renderChatHistory(chatHistorySearchInput.value); // Re-render to reflect new tags
2073
+ }
2074
+ });
2075
+
2076
+ tagCancelButton.addEventListener('click', hideTagModal);
2077
+
2078
+ // Close Tag Modal on overlay click
2079
+ tagModal.addEventListener('click', function(event) {
2080
+ if (event.target === tagModal) {
2081
+ hideTagModal();
2082
+ }
2083
+ });
2084
+
2085
+ // Initial UI setup
2086
+ updateProfileUI(); // Call once on load to populate existing data
2087
+ });
2088
+ </script>
2089
+ </body>
2090
+ </html>