| <!DOCTYPE html> |
| <html lang="en" data-bs-theme="dark"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>GAKR AI - Profile</title> |
| <script> |
| |
| if (localStorage.getItem('isLoggedIn') !== 'true') { |
| window.location.href = '/auth'; |
| } |
| </script> |
| <link rel="stylesheet" href="https://cdn.replit.com/agent/bootstrap-agent-dark-theme.min.css"> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css"> |
| <style> |
| |
| |
| |
| |
| |
| :root { |
| --gakr-blue: #007bff; |
| --gakr-blue-dark: #0056b3; |
| --gakr-blue-light: #e0f0ff; |
| --gakr-blue-rgb: 0, 123, 255; |
| |
| |
| --bs-body-bg: #1a1a1a; |
| --bs-body-color: #f0f0f0; |
| --bs-primary: var(--gakr-blue); |
| --bs-secondary: #6c757d; |
| --bs-secondary-bg: #2a2a2a; |
| --bs-tertiary-bg: #3a3a3a; |
| --bs-border-color: #4a4a4a; |
| --bs-link-color: var(--gakr-blue); |
| --bs-link-hover-color: var(--gakr-blue-dark); |
| --bs-heading-color: #f8f9fa; |
| --bs-tertiary-color: #adb5bd; |
| --bs-success: #28a745; |
| --bs-info: #17a2b8; |
| --bs-warning: #ffc107; |
| --bs-danger: #dc3545; |
| } |
| |
| body { |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| background-color: var(--bs-body-bg); |
| color: var(--bs-body-color); |
| margin: 0; |
| padding-top: 64px; |
| display: flex; |
| flex-direction: column; |
| min-height: 100vh; |
| } |
| |
| .gakr-layout { |
| display: flex; |
| flex-direction: column; |
| min-height: 100vh; |
| width: 100%; |
| max-width: 1200px; |
| margin: 0 auto; |
| box-shadow: 0 0 20px rgba(0, 0, 0, 0.5); |
| background-color: var(--bs-body-bg); |
| } |
| |
| |
| |
| |
| |
| |
| .gakr-chat-header { |
| padding: 0.75rem 1.5rem; |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| border-bottom: 1px solid var(--bs-border-color); |
| height: 64px; |
| position: fixed; |
| top: 0; |
| left: 0; |
| right: 0; |
| width: 100%; |
| background-color: var(--bs-body-bg); |
| z-index: 10; |
| } |
| |
| .gakr-logo-area { |
| display: flex; |
| align-items: center; |
| gap: 0.75rem; |
| } |
| |
| .gakr-brand-logo { |
| width: 24px; |
| height: 24px; |
| display: flex; |
| align-items: center; |
| justify-content: center; |
| } |
| |
| .gakr-brand-text { |
| font-size: 1.25rem; |
| font-weight: 500; |
| color: var(--gakr-blue); |
| } |
| |
| .gakr-nav-controls { |
| display: flex; |
| align-items: center; |
| } |
| |
| .gakr-login-button { |
| color: var(--gakr-blue); |
| text-decoration: none; |
| background: transparent; |
| border: 1px solid var(--gakr-blue); |
| padding: 0.5rem 1rem; |
| border-radius: 50px; |
| font-size: 0.9rem; |
| transition: background-color 0.2s; |
| } |
| |
| .gakr-login-button:hover { |
| background-color: rgba(66, 133, 244, 0.1); |
| } |
| |
| .gakr-profile-button { |
| color: var(--gakr-blue); |
| text-decoration: none; |
| background: transparent; |
| border: none; |
| padding: 0.5rem 1rem; |
| border-radius: 50px; |
| font-size: 0.9rem; |
| transition: background-color 0.2s; |
| display: flex; |
| align-items: center; |
| gap: 0.5rem; |
| } |
| |
| .gakr-profile-button:hover { |
| background-color: rgba(66, 133, 244, 0.1); |
| } |
| |
| |
| |
| |
| |
| |
| .profile-nav-controls { |
| position: relative; |
| z-index: 1001; |
| } |
| |
| .profile-button { |
| width: 40px; |
| height: 40px; |
| border-radius: 50%; |
| background-color: var(--gakr-blue); |
| color: white; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| font-size: 1.2rem; |
| font-weight: bold; |
| cursor: pointer; |
| transition: background-color 0.2s ease; |
| box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); |
| } |
| |
| .profile-button:hover { |
| background-color: var(--gakr-blue-dark); |
| } |
| |
| .profile-dropdown-menu { |
| position: absolute; |
| top: calc(100% + 10px); |
| right: 0; |
| background-color: var(--bs-tertiary-bg); |
| border: 1px solid var(--bs-border-color); |
| border-radius: 8px; |
| min-width: 180px; |
| box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); |
| opacity: 0; |
| visibility: hidden; |
| transform: translateY(-10px); |
| transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease; |
| padding: 0.5rem 0; |
| z-index: 100; |
| } |
| |
| .profile-dropdown-menu.show { |
| opacity: 1; |
| visibility: visible; |
| transform: translateY(0); |
| } |
| |
| .profile-dropdown-item { |
| display: block; |
| padding: 0.8rem 1.2rem; |
| color: var(--bs-body-color); |
| text-decoration: none; |
| font-size: 0.95rem; |
| transition: background-color 0.2s ease, color 0.2s ease; |
| } |
| |
| .profile-dropdown-item:hover { |
| background-color: var(--bs-secondary-bg); |
| color: var(--gakr-blue); |
| } |
| |
| |
| |
| |
| |
| |
| .profile-view { |
| flex-grow: 1; |
| padding: 2rem; |
| background-color: var(--bs-body-bg); |
| display: flex; |
| flex-direction: column; |
| gap: 2rem; |
| } |
| |
| .profile-header-section { |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| text-align: center; |
| margin-bottom: 2rem; |
| padding-bottom: 1.5rem; |
| border-bottom: 1px solid var(--bs-border-color); |
| } |
| |
| .profile-avatar-large { |
| width: 120px; |
| height: 120px; |
| border-radius: 50%; |
| background-color: var(--gakr-blue); |
| color: white; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| font-size: 4rem; |
| font-weight: bold; |
| margin-bottom: 1rem; |
| border: 4px solid var(--bs-tertiary-bg); |
| box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); |
| position: relative; |
| overflow: hidden; |
| } |
| |
| .profile-avatar-large img { |
| width: 100%; |
| height: 100%; |
| object-fit: cover; |
| border-radius: 50%; |
| } |
| |
| .profile-avatar-edit-icon { |
| position: absolute; |
| bottom: 5px; |
| right: 5px; |
| background-color: var(--bs-tertiary-bg); |
| border-radius: 50%; |
| width: 30px; |
| height: 30px; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| cursor: pointer; |
| box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); |
| transition: background-color 0.2s ease; |
| color: var(--bs-body-color); |
| } |
| |
| .profile-avatar-edit-icon:hover { |
| background-color: var(--bs-secondary-bg); |
| color: var(--gakr-blue); |
| } |
| |
| .profile-username-container { |
| display: flex; |
| align-items: center; |
| gap: 0.5rem; |
| margin-bottom: 0.5rem; |
| } |
| |
| .profile-username { |
| font-size: 2.2rem; |
| font-weight: 700; |
| color: var(--bs-heading-color); |
| margin: 0; |
| line-height: 1; |
| } |
| |
| .username-edit-icon, .section-edit-icon { |
| color: var(--bs-secondary-color); |
| cursor: pointer; |
| font-size: 1.1rem; |
| transition: color 0.2s ease; |
| } |
| |
| .username-edit-icon:hover, .section-edit-icon:hover { |
| color: var(--gakr-blue); |
| } |
| |
| .username-input-container { |
| display: none; |
| align-items: center; |
| gap: 0.5rem; |
| margin-bottom: 0.5rem; |
| width: 100%; |
| max-width: 300px; |
| } |
| |
| .username-edit-input { |
| flex-grow: 1; |
| padding: 0.6rem 0.8rem; |
| border-radius: 8px; |
| border: 1px solid var(--bs-border-color); |
| background-color: var(--bs-secondary-bg); |
| color: var(--bs-body-color); |
| font-size: 1.1rem; |
| outline: none; |
| box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2); |
| } |
| .username-edit-input::placeholder { |
| color: var(--bs-tertiary-color); |
| } |
| .username-edit-input:focus { |
| border-color: var(--gakr-blue); |
| box-shadow: 0 0 0 0.25rem rgba(var(--gakr-blue-rgb), 0.25); |
| } |
| |
| |
| .username-save-button { |
| padding: 0.6rem 1.2rem; |
| border-radius: 8px; |
| background-color: var(--gakr-blue); |
| color: white; |
| border: none; |
| cursor: pointer; |
| font-size: 1rem; |
| transition: background-color 0.2s ease; |
| } |
| |
| .username-save-button:hover { |
| background-color: var(--gakr-blue-dark); |
| } |
| |
| .profile-email { |
| font-size: 1.1rem; |
| color: var(--bs-secondary-color); |
| margin: 0; |
| } |
| |
| .profile-section { |
| background-color: var(--bs-secondary-bg); |
| border-radius: 12px; |
| padding: 1.5rem 2rem; |
| box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15); |
| display: flex; |
| flex-direction: column; |
| gap: 1rem; |
| } |
| |
| .profile-section-title { |
| font-size: 1.6rem; |
| color: var(--bs-heading-color); |
| margin-top: 0; |
| margin-bottom: 1rem; |
| padding-bottom: 0.5rem; |
| border-bottom: 1px solid var(--bs-border-color); |
| display: flex; |
| align-items: center; |
| gap: 0.75rem; |
| } |
| |
| .profile-info-item { |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| padding: 0.5rem 0; |
| border-bottom: 1px dashed var(--bs-border-color); |
| } |
| .profile-info-item:last-child { |
| border-bottom: none; |
| } |
| |
| .profile-info-label { |
| font-weight: 500; |
| color: var(--bs-tertiary-color); |
| flex-basis: 30%; |
| } |
| |
| .profile-info-value { |
| color: var(--bs-body-color); |
| text-align: right; |
| flex-basis: 70%; |
| } |
| |
| |
| .profile-about-me-display { |
| color: var(--bs-body-color); |
| line-height: 1.6; |
| margin-bottom: 1rem; |
| background-color: var(--bs-body-bg); |
| border: 1px solid var(--bs-border-color); |
| border-radius: 8px; |
| padding: 1rem; |
| min-height: 80px; |
| overflow-y: auto; |
| } |
| .profile-about-me-display.placeholder { |
| color: var(--bs-tertiary-color); |
| font-style: italic; |
| } |
| |
| .about-me-edit-icon { |
| margin-left: auto; |
| } |
| |
| .about-me-input-container { |
| display: none; |
| flex-direction: column; |
| gap: 0.75rem; |
| } |
| |
| .about-me-edit-textarea { |
| width: 100%; |
| padding: 0.8rem; |
| border-radius: 8px; |
| border: 1px solid var(--bs-border-color); |
| background-color: var(--bs-body-bg); |
| color: var(--bs-body-color); |
| font-size: 1rem; |
| min-height: 120px; |
| resize: vertical; |
| outline: none; |
| box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2); |
| } |
| .about-me-edit-textarea::placeholder { |
| color: var(--bs-tertiary-color); |
| } |
| .about-me-edit-textarea:focus { |
| border-color: var(--gakr-blue); |
| box-shadow: 0 0 0 0.25rem rgba(var(--gakr-blue-rgb), 0.25); |
| } |
| |
| .about-me-save-button { |
| align-self: flex-end; |
| padding: 0.7rem 1.5rem; |
| border-radius: 8px; |
| background-color: var(--gakr-blue); |
| color: white; |
| border: none; |
| cursor: pointer; |
| font-size: 1rem; |
| transition: background-color 0.2s ease; |
| } |
| |
| .about-me-save-button:hover { |
| background-color: var(--gakr-blue-dark); |
| } |
| |
| |
| .profile-chat-history { |
| display: flex; |
| flex-direction: column; |
| gap: 1rem; |
| } |
| |
| .chat-history-item { |
| background-color: var(--bs-body-bg); |
| border: 1px solid var(--bs-border-color); |
| border-radius: 12px; |
| padding: 1rem 1.2rem; |
| cursor: pointer; |
| transition: background-color 0.2s ease, transform 0.2s ease, box-shadow 0.2s ease; |
| position: relative; |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); |
| } |
| |
| .chat-history-item:hover { |
| background-color: var(--bs-secondary-bg); |
| transform: translateY(-2px); |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); |
| } |
| |
| .chat-history-item .chat-title-wrapper { |
| display: flex; |
| align-items: center; |
| gap: 0.5rem; |
| margin-bottom: 0.5rem; |
| } |
| |
| .chat-history-item .chat-title { |
| font-size: 1.15rem; |
| font-weight: 600; |
| color: var(--bs-heading-color); |
| flex-grow: 1; |
| } |
| |
| .chat-history-item .chat-meta { |
| font-size: 0.85rem; |
| color: var(--bs-secondary-color); |
| display: flex; |
| justify-content: space-between; |
| align-items: center; |
| margin-bottom: 0.5rem; |
| } |
| |
| .empty-chat-history { |
| text-align: center; |
| color: var(--bs-tertiary-color); |
| font-style: italic; |
| padding: 2rem; |
| } |
| |
| |
| .profile-buttons-area { |
| display: flex; |
| flex-direction: column; |
| align-items: center; |
| gap: 1rem; |
| margin-top: 2rem; |
| padding-top: 2rem; |
| border-top: 1px solid var(--bs-border-color); |
| } |
| |
| .gakr-button-primary, .gakr-button-secondary { |
| display: inline-flex; |
| align-items: center; |
| justify-content: center; |
| padding: 0.8rem 1.5rem; |
| border-radius: 10px; |
| font-size: 1rem; |
| font-weight: 500; |
| text-decoration: none; |
| transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease; |
| width: 100%; |
| max-width: 300px; |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); |
| } |
| |
| .gakr-button-primary { |
| background-color: var(--gakr-blue); |
| color: white; |
| border: 1px solid var(--gakr-blue); |
| } |
| |
| .gakr-button-primary:hover { |
| background-color: var(--gakr-blue-dark); |
| border-color: var(--gakr-blue-dark); |
| color: white; |
| } |
| |
| .gakr-button-secondary { |
| background-color: transparent; |
| color: var(--gakr-blue); |
| border: 1px solid var(--gakr-blue); |
| } |
| |
| .gakr-button-secondary:hover { |
| background-color: var(--gakr-blue-light); |
| color: var(--gakr-blue-dark); |
| border-color: var(--gakr-blue-dark); |
| } |
| |
| .me-2 { |
| margin-right: 0.5rem; |
| } |
| |
| |
| |
| |
| |
| |
| .gakr-footer { |
| padding: 1rem 1.5rem; |
| background-color: var(--bs-tertiary-bg); |
| border-top: 1px solid var(--bs-border-color); |
| text-align: center; |
| font-size: 0.9rem; |
| color: var(--bs-secondary-color); |
| margin-top: auto; |
| } |
| |
| |
| |
| |
| |
| |
| .toast-container { |
| position: fixed; |
| bottom: 20px; |
| left: 50%; |
| transform: translateX(-50%); |
| display: flex; |
| flex-direction: column; |
| gap: 10px; |
| z-index: 2000; |
| } |
| |
| .gakr-toast { |
| background-color: var(--bs-tertiary-bg); |
| color: var(--bs-body-color); |
| padding: 12px 20px; |
| border-radius: 8px; |
| box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); |
| display: flex; |
| align-items: center; |
| gap: 10px; |
| min-width: 250px; |
| opacity: 0; |
| transform: translateY(20px); |
| transition: opacity 0.3s ease-out, transform 0.3s ease-out; |
| border: 1px solid var(--bs-border-color); |
| } |
| |
| .gakr-toast.show { |
| opacity: 1; |
| transform: translateY(0); |
| } |
| |
| .gakr-toast.hide { |
| opacity: 0; |
| transform: translateY(20px); |
| } |
| |
| .gakr-toast .toast-icon { |
| font-size: 1.2rem; |
| } |
| |
| .gakr-toast.success { |
| border-left: 5px solid var(--bs-success); |
| } |
| .gakr-toast.success .toast-icon { |
| color: var(--bs-success); |
| } |
| |
| .gakr-toast.error { |
| border-left: 5px solid var(--bs-danger); |
| } |
| .gakr-toast.error .toast-icon { |
| color: var(--bs-danger); |
| } |
| |
| .gakr-toast.warning { |
| border-left: 5px solid var(--bs-warning); |
| } |
| .gakr-toast.warning .toast-icon { |
| color: var(--bs-warning); |
| } |
| |
| .gakr-toast.info { |
| border-left: 5px solid var(--bs-info); |
| } |
| .gakr-toast.info .toast-icon { |
| color: var(--bs-info); |
| } |
| |
| |
| |
| |
| |
| |
| |
| .gakr-modal-overlay { |
| position: fixed; |
| top: 0; |
| left: 0; |
| width: 100%; |
| height: 100%; |
| background-color: rgba(0, 0, 0, 0.6); |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| z-index: 1500; |
| opacity: 0; |
| visibility: hidden; |
| transition: opacity 0.3s ease, visibility 0.3s ease; |
| } |
| |
| .gakr-modal-overlay.show { |
| opacity: 1; |
| visibility: visible; |
| } |
| |
| .gakr-modal-content { |
| background-color: var(--bs-body-bg); |
| border: 1px solid var(--bs-border-color); |
| border-radius: 12px; |
| box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); |
| padding: 2rem; |
| width: 90%; |
| max-width: 450px; |
| text-align: center; |
| transform: translateY(-20px) scale(0.95); |
| opacity: 0; |
| transition: transform 0.3s ease-out, opacity 0.3s ease-out; |
| } |
| |
| .gakr-modal-overlay.show .gakr-modal-content { |
| transform: translateY(0) scale(1); |
| opacity: 1; |
| } |
| |
| .gakr-modal-title { |
| font-size: 1.5rem; |
| font-weight: 600; |
| margin-bottom: 1rem; |
| color: var(--bs-heading-color); |
| } |
| |
| .gakr-modal-message { |
| font-size: 1rem; |
| color: var(--bs-body-color); |
| margin-bottom: 1.5rem; |
| line-height: 1.5; |
| } |
| |
| .gakr-modal-input { |
| width: calc(100% - 20px); |
| padding: 0.8rem 10px; |
| margin-bottom: 1.5rem; |
| border: 1px solid var(--bs-border-color); |
| border-radius: 8px; |
| background-color: var(--bs-secondary-bg); |
| color: var(--bs-body-color); |
| font-size: 1rem; |
| outline: none; |
| } |
| |
| .gakr-modal-input:focus { |
| border-color: var(--gakr-blue); |
| box-shadow: 0 0 0 0.2rem rgba(var(--gakr-blue-rgb), 0.25); |
| } |
| |
| .gakr-modal-buttons { |
| display: flex; |
| justify-content: center; |
| gap: 1rem; |
| } |
| |
| .gakr-modal-buttons button { |
| padding: 0.7rem 1.5rem; |
| border-radius: 8px; |
| font-size: 1rem; |
| cursor: pointer; |
| transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out; |
| } |
| |
| .confirm-ok, .prompt-submit { |
| background-color: var(--gakr-blue); |
| color: white; |
| border: none; |
| } |
| |
| .confirm-ok:hover, .prompt-submit:hover { |
| background-color: var(--gakr-blue-dark); |
| } |
| |
| .confirm-cancel, .prompt-cancel { |
| background-color: transparent; |
| color: var(--gakr-blue); |
| border: 1px solid var(--gakr-blue); |
| } |
| |
| .confirm-cancel:hover, .prompt-cancel:hover { |
| background-color: var(--gakr-blue-light); |
| color: var(--gakr-blue-dark); |
| border-color: var(--gakr-blue-dark); |
| } |
| |
| |
| |
| |
| |
| |
| .chat-history-item.pinned { |
| background-color: var(--bs-secondary-bg); |
| border-left: 5px solid var(--gakr-blue); |
| padding-left: calc(1rem - 5px); |
| } |
| .pinned-icon { |
| color: var(--gakr-blue); |
| font-size: 0.9em; |
| margin-left: 0.5rem; |
| vertical-align: middle; |
| } |
| |
| .chat-history-item .chat-tags { |
| font-size: 0.8rem; |
| color: var(--bs-secondary-color); |
| margin-top: 0.5rem; |
| } |
| .chat-history-item .chat-tags span { |
| background-color: rgba(var(--gakr-blue-rgb), 0.1); |
| color: var(--gakr-blue); |
| padding: 0.2rem 0.6rem; |
| border-radius: 12px; |
| margin-right: 0.4rem; |
| display: inline-block; |
| margin-bottom: 0.4rem; |
| white-space: nowrap; |
| } |
| .chat-history-item .chat-tags span:last-child { |
| margin-right: 0; |
| } |
| |
| .chat-history-search-container { |
| position: relative; |
| margin-bottom: 1rem; |
| width: 100%; |
| max-width: 400px; |
| align-self: center; |
| } |
| .chat-history-search-input { |
| width: 100%; |
| padding: 0.6rem 2.5rem 0.6rem 1rem; |
| border-radius: 8px; |
| border: 1px solid var(--bs-border-color); |
| background-color: var(--bs-body-bg); |
| color: var(--bs-body-color); |
| font-size: 0.95rem; |
| outline: none; |
| } |
| .chat-history-search-input:focus { |
| border-color: var(--gakr-blue); |
| box-shadow: 0 0 0 0.25rem rgba(var(--gakr-blue-rgb), 0.25); |
| } |
| .chat-history-search-icon { |
| position: absolute; |
| right: 1rem; |
| top: 50%; |
| transform: translateY(-50%); |
| color: var(--bs-secondary-color); |
| pointer-events: none; |
| } |
| |
| |
| .chat-history-sort-container { |
| margin-bottom: 1rem; |
| display: flex; |
| justify-content: flex-end; |
| align-items: center; |
| gap: 0.5rem; |
| font-size: 0.9rem; |
| color: var(--bs-secondary-color); |
| } |
| .chat-history-sort-select { |
| background-color: var(--bs-tertiary-bg); |
| color: var(--bs-body-color); |
| border: 1px solid var(--bs-border-color); |
| border-radius: 8px; |
| padding: 0.3rem 0.6rem; |
| font-size: 0.9rem; |
| cursor: pointer; |
| outline: none; |
| -webkit-appearance: none; |
| -moz-appearance: none; |
| appearance: none; |
| 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"); |
| background-repeat: no-repeat; |
| background-position: right 0.5rem center; |
| background-size: 1em; |
| } |
| .chat-history-sort-select:focus { |
| border-color: var(--gakr-blue); |
| box-shadow: 0 0 0 0.25rem rgba(var(--gakr-blue-rgb), 0.25); |
| } |
| |
| .chat-history-last-active { |
| font-size: 0.75rem; |
| color: var(--bs-tertiary-color); |
| margin-left: auto; |
| } |
| |
| |
| .chat-context-menu { |
| position: fixed; |
| background-color: var(--bs-tertiary-bg); |
| border: 1px solid var(--bs-border-color); |
| border-radius: 8px; |
| box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); |
| min-width: 150px; |
| padding: 0.5rem 0; |
| z-index: 1002; |
| opacity: 0; |
| visibility: hidden; |
| transform: scale(0.95); |
| transition: opacity 0.15s ease, transform 0.15s ease, visibility 0.15s ease; |
| } |
| |
| .chat-context-menu.show { |
| opacity: 1; |
| visibility: visible; |
| transform: scale(1); |
| } |
| |
| .chat-context-menu-item { |
| display: flex; |
| align-items: center; |
| gap: 0.8rem; |
| padding: 0.8rem 1.2rem; |
| color: var(--bs-body-color); |
| text-decoration: none; |
| font-size: 0.9rem; |
| transition: background-color 0.2s ease, color 0.2s ease; |
| } |
| |
| .chat-context-menu-item:hover { |
| background-color: var(--bs-secondary-bg); |
| color: var(--gakr-blue); |
| } |
| |
| .chat-context-menu-item i { |
| width: 1.2rem; |
| text-align: center; |
| } |
| |
| |
| .tag-selection-modal-content { |
| background-color: var(--bs-body-bg); |
| border: 1px solid var(--bs-border-color); |
| border-radius: 12px; |
| box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); |
| padding: 1.5rem; |
| width: 90%; |
| max-width: 500px; |
| text-align: left; |
| transform: translateY(-20px) scale(0.95); |
| opacity: 0; |
| transition: transform 0.3s ease-out, opacity 0.3s ease-out; |
| } |
| .gakr-modal-overlay.show .tag-selection-modal-content { |
| transform: translateY(0) scale(1); |
| opacity: 1; |
| } |
| .tag-selection-modal-title { |
| font-size: 1.3rem; |
| font-weight: 600; |
| margin-bottom: 1rem; |
| color: var(--bs-heading-color); |
| padding-bottom: 0.5rem; |
| border-bottom: 1px solid var(--bs-border-color); |
| } |
| .tag-options-grid { |
| display: grid; |
| grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); |
| gap: 0.75rem; |
| margin-bottom: 1.5rem; |
| } |
| .tag-option { |
| background-color: var(--bs-secondary-bg); |
| color: var(--bs-body-color); |
| border: 1px solid var(--bs-border-color); |
| border-radius: 8px; |
| padding: 0.5rem 0.8rem; |
| cursor: pointer; |
| text-align: center; |
| transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease; |
| font-size: 0.9rem; |
| display: flex; |
| justify-content: center; |
| align-items: center; |
| } |
| .tag-option:hover { |
| background-color: var(--bs-tertiary-bg); |
| border-color: var(--gakr-blue); |
| } |
| .tag-option.selected { |
| background-color: var(--gakr-blue); |
| color: white; |
| border-color: var(--gakr-blue-dark); |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); |
| } |
| .tag-option.selected:hover { |
| background-color: var(--gakr-blue-dark); |
| border-color: var(--gakr-blue-dark); |
| } |
| .tag-actions { |
| display: flex; |
| justify-content: flex-end; |
| gap: 1rem; |
| padding-top: 1rem; |
| border-top: 1px solid var(--bs-border-color); |
| } |
| .tag-actions button { |
| padding: 0.6rem 1rem; |
| border-radius: 8px; |
| font-size: 1rem; |
| cursor: pointer; |
| transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out; |
| } |
| .tag-actions .confirm-ok { |
| background-color: var(--gakr-blue); |
| color: white; |
| border: none; |
| } |
| .tag-actions .confirm-ok:hover { |
| background-color: var(--gakr-blue-dark); |
| } |
| .tag-actions .confirm-cancel { |
| background-color: transparent; |
| color: var(--gakr-blue); |
| border: 1px solid var(--gakr-blue); |
| } |
| .tag-actions .confirm-cancel:hover { |
| background-color: var(--gakr-blue-light); |
| color: var(--gakr-blue-dark); |
| border-color: var(--gakr-blue-dark); |
| } |
| |
| |
| |
| |
| |
| |
| |
| @media (max-width: 768px) { |
| .gakr-header { |
| padding: 0.8rem 1rem; |
| } |
| .profile-view { |
| padding: 1rem; |
| gap: 1.5rem; |
| } |
| .profile-header-section { |
| margin-bottom: 1.5rem; |
| } |
| .profile-username { |
| font-size: 1.8rem; |
| } |
| .profile-email { |
| font-size: 1rem; |
| } |
| .profile-section { |
| padding: 1rem 1.2rem; |
| } |
| .profile-section-title { |
| font-size: 1.4rem; |
| } |
| .profile-info-item { |
| flex-direction: column; |
| align-items: flex-start; |
| gap: 0.2rem; |
| } |
| .profile-info-label { |
| flex-basis: auto; |
| font-size: 0.9rem; |
| } |
| .profile-info-value { |
| flex-basis: auto; |
| text-align: left; |
| font-size: 0.95rem; |
| } |
| .profile-buttons-area { |
| margin-top: 1.5rem; |
| padding-top: 1.5rem; |
| } |
| .gakr-button-primary, .gakr-button-secondary { |
| max-width: none; |
| } |
| .chat-history-sort-container { |
| justify-content: flex-start; |
| } |
| .tag-options-grid { |
| grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); |
| gap: 0.5rem; |
| } |
| .tag-option { |
| padding: 0.4rem 0.6rem; |
| font-size: 0.8rem; |
| } |
| } |
| |
| @media (max-width: 480px) { |
| .gakr-brand-text { |
| font-size: 1.3rem; |
| } |
| .profile-avatar-large { |
| width: 90px; |
| height: 90px; |
| font-size: 3rem; |
| } |
| .profile-username { |
| font-size: 1.5rem; |
| } |
| .username-edit-input, .username-save-button { |
| font-size: 0.9rem; |
| padding: 0.5rem 0.8rem; |
| } |
| .about-me-edit-textarea, .about-me-save-button { |
| font-size: 0.9rem; |
| padding: 0.6rem 1rem; |
| } |
| .chat-history-item { |
| padding: 0.8rem 1rem; |
| } |
| .chat-history-item .chat-title { |
| font-size: 1rem; |
| } |
| .chat-history-item .chat-meta { |
| font-size: 0.75rem; |
| } |
| .chat-history-sort-select { |
| font-size: 0.85rem; |
| padding: 0.2rem 0.5rem; |
| } |
| .gakr-modal-content { |
| padding: 1.5rem; |
| } |
| .gakr-modal-title { |
| font-size: 1.3rem; |
| } |
| .gakr-modal-message { |
| font-size: 0.9rem; |
| } |
| .gakr-modal-buttons button { |
| padding: 0.5rem 1rem; |
| font-size: 0.9rem; |
| } |
| } |
| </style> |
| </head> |
| <body class="on-profile-page"> |
| <div class="gakr-layout"> |
| <header class="gakr-chat-header"> |
| <div class="gakr-logo-area"> |
| <a href="/" class="gakr-brand-logo"> |
| <i class="fas fa-robot" style="color: var(--gakr-blue);"></i> |
| </a> |
| <a href="/" class="gakr-brand-text" style="text-decoration: none;">GAKR AI</a> |
| </div> |
|
|
| <div class="gakr-nav-controls" id="authButtonContainer"> |
| <a href="/auth" class="gakr-login-button">Sign in / Register</a> |
| </div> |
| </header> |
|
|
| <div class="profile-view" id="profileView"> |
| <div class="profile-header-section"> |
| <div class="profile-avatar-large" id="profileAvatarLarge"> |
| U |
| <label for="avatarUpload" class="profile-avatar-edit-icon" title="Change Avatar"> |
| <i class="fas fa-camera"></i> |
| <input type="file" id="avatarUpload" accept="image/*" style="display: none;"> |
| </label> |
| </div> |
| <div class="profile-username-container"> |
| <h1 class="profile-username" id="profileUsernameDisplay">User Name</h1> |
| <i class="fas fa-pencil-alt username-edit-icon" id="editUsernameIcon" title="Edit Username"></i> |
| </div> |
| <div class="username-input-container" id="usernameInputContainer"> |
| <input type="text" id="usernameEditInput" class="username-edit-input" placeholder="Enter new username"> |
| <button id="usernameSaveButton" class="username-save-button">Save</button> |
| </div> |
| <p class="profile-email" id="profileEmailDisplay">user.email@example.com</p> |
| </div> |
|
|
| <div class="profile-section"> |
| <h2 class="profile-section-title">Account Information</h2> |
| <div class="profile-info-item"> |
| <span class="profile-info-label">Name:</span> |
| <span class="profile-info-value" id="accountName">User Name</span> |
| </div> |
| <div class="profile-info-item"> |
| <span class="profile-info-label">Email:</span> |
| <span class="profile-info-value" id="accountEmail">user.email@example.com</span> |
| </div> |
| </div> |
|
|
| <div class="profile-section"> |
| <h2 class="profile-section-title"> |
| About Me |
| <i class="fas fa-pencil-alt section-edit-icon about-me-edit-icon" id="editAboutMeIcon" title="Edit About Me"></i> |
| </h2> |
| <div class="profile-about-me-display" id="profileAboutMeDisplay">Tell us something about yourself...</div> |
| <div class="about-me-input-container" id="aboutMeInputContainer"> |
| <textarea id="aboutMeEditInput" class="about-me-edit-textarea" placeholder="Write a short bio..."></textarea> |
| <button id="aboutMeSaveButton" class="about-me-save-button">Save</button> |
| </div> |
| </div> |
|
|
| <div class="profile-section"> |
| <h2 class="profile-section-title">Recent Conversations</h2> |
| <div class="chat-history-search-container"> |
| <input type="text" id="chatHistorySearchInput" class="chat-history-search-input" placeholder="Search conversations..."> |
| <i class="fas fa-search chat-history-search-icon"></i> |
| </div> |
| <div class="profile-chat-history" id="chatHistoryList"> |
| <p class="empty-chat-history" id="emptyChatHistoryMessage" style="display: none;">No conversations yet. Start chatting with GAKR AI!</p> |
| </div> |
| </div> |
|
|
| <div class="profile-buttons-area"> |
| <a href="/chat" class="gakr-button-primary" id="startChattingButton"> |
| <i class="fas fa-comments me-2"></i> Start Chatting with GAKR AI |
| </a> |
| <button |
| type="button" |
| class="gakr-button-primary" |
| id="profileSettingsButton"> |
| <i class="fas fa-cog me-2"></i> Settings |
| </button> |
|
|
|
|
| <a href="#" class="gakr-button-secondary" id="profileLogoutButton" onclick="logoutFunction()"> |
| <i class="fas fa-sign-out-alt me-2"></i> Log out |
| </a> |
| </div> |
| </div> |
|
|
| <footer class="gakr-footer"> |
| <div>GAKR AI - Powered by Advanced Local AI</div> |
| </footer> |
|
|
| <div id="chatContextMenu" class="chat-context-menu"> |
| <a href="#" class="chat-context-menu-item" data-action="delete"><i class="fas fa-trash-alt"></i> Delete</a> |
| <a href="#" class="chat-context-menu-item" data-action="share"><i class="fas fa-share-alt"></i> Share</a> |
| <a href="#" class="chat-context-menu-item pin-unpin" data-action="pin"><i class="fas fa-thumbtack"></i> Pin</a> |
| <a href="#" class="chat-context-menu-item" data-action="tag"><i class="fas fa-tag"></i> Add Tag</a> |
| </div> |
| </div> |
|
|
| <div class="toast-container" id="toastContainer"></div> |
|
|
| <div class="gakr-modal-overlay" id="confirmModal"> |
| <div class="gakr-modal-content"> |
| <h3 class="gakr-modal-title" id="confirmTitle">Confirm Action</h3> |
| <p class="gakr-modal-message" id="confirmMessage">Are you sure you want to proceed?</p> |
| <div class="gakr-modal-buttons"> |
| <button class="confirm-cancel" id="confirmCancelButton">Cancel</button> |
| <button class="confirm-ok" id="confirmOkButton">Confirm</button> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="gakr-modal-overlay" id="promptModal"> |
| <div class="gakr-modal-content"> |
| <h3 class="gakr-modal-title" id="promptTitle">Enter Information</h3> |
| <p class="gakr-modal-message" id="promptMessage">Please provide the required input:</p> |
| <input type="text" class="gakr-modal-input" id="promptInput" placeholder="Enter text here..."> |
| <div class="gakr-modal-buttons"> |
| <button class="prompt-cancel" id="promptCancelButton">Cancel</button> |
| <button class="prompt-submit" id="promptSubmitButton">Submit</button> |
| </div> |
| </div> |
| </div> |
|
|
| <div class="gakr-modal-overlay" id="tagModal"> |
| <div class="tag-selection-modal-content"> |
| <h3 class="tag-selection-modal-title" id="tagModalTitle">Add Tag to Conversation</h3> |
| <div class="tag-options-grid" id="tagOptions"> |
| </div> |
| <div class="tag-actions"> |
| <button class="confirm-cancel" id="tagCancelButton">Cancel</button> |
| <button class="confirm-ok" id="tagSaveButton">Save</button> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| let confirmModal, confirmTitle, confirmMessage, confirmOkButton, confirmCancelButton; |
| |
| |
| async function logoutFunction() { |
| const confirmed = await showConfirmDialog('Are you sure you want to log out?', 'Logout Confirmation'); |
| if (confirmed) { |
| localStorage.removeItem('isLoggedIn'); |
| sessionStorage.clear(); |
| window.location.href = '/'; |
| } |
| } |
| |
| |
| function showConfirmDialog(message, title = 'Confirm Action') { |
| return new Promise((resolve) => { |
| if (!confirmModal) { |
| |
| const confirmed = window.confirm(message); |
| resolve(confirmed); |
| return; |
| } |
| |
| confirmTitle.textContent = title; |
| confirmMessage.textContent = message; |
| confirmModal.classList.add('show'); |
| |
| |
| const triggeringElement = document.activeElement; |
| |
| const handleOk = () => { |
| confirmModal.classList.remove('show'); |
| confirmOkButton.removeEventListener('click', handleOk); |
| confirmCancelButton.removeEventListener('click', handleCancel); |
| if (triggeringElement) triggeringElement.focus(); |
| resolve(true); |
| }; |
| |
| const handleCancel = () => { |
| confirmModal.classList.remove('show'); |
| confirmOkButton.removeEventListener('click', handleOk); |
| confirmCancelButton.removeEventListener('click', handleCancel); |
| if (triggeringElement) triggeringElement.focus(); |
| resolve(false); |
| }; |
| |
| confirmOkButton.addEventListener('click', handleOk); |
| confirmCancelButton.addEventListener('click', handleCancel); |
| |
| |
| confirmModal.addEventListener('click', function(event) { |
| if (event.target === confirmModal) { |
| handleCancel(); |
| } |
| }, { once: true }); |
| }); |
| } |
| |
| document.addEventListener('DOMContentLoaded', function() { |
| |
| function updateAuthButton() { |
| const authButtonContainer = document.getElementById('authButtonContainer'); |
| const userIsLoggedIn = localStorage.getItem('isLoggedIn') === 'true'; |
| |
| if (userIsLoggedIn) { |
| authButtonContainer.innerHTML = ` |
| <a href="/profile" class="gakr-profile-button"> |
| <i class="fas fa-user-circle me-2"></i>Profile |
| </a> |
| `; |
| const profileButton = authButtonContainer.querySelector('.gakr-profile-button'); |
| if (profileButton) { |
| profileButton.addEventListener('click', function(event) { |
| console.log("Profile button clicked!"); |
| }); |
| } |
| } else { |
| authButtonContainer.innerHTML = ` |
| <a href="/auth" class="gakr-login-button">Sign in / Register</a> |
| `; |
| } |
| } |
| |
| updateAuthButton(); |
| window.addEventListener('focus', updateAuthButton); |
| window.addEventListener('storage', updateAuthButton); |
| |
| |
| if (localStorage.getItem('isLoggedIn') !== 'true') { |
| window.location.href = '/auth'; |
| return; |
| } |
| |
| const profileButton = document.getElementById('profileButton'); |
| const profileDropdown = document.getElementById('profileDropdown'); |
| const logoutButton = document.getElementById('logoutButton'); |
| const brandLogoArea = document.getElementById('brandLogoArea'); |
| const signInRegisterButton = document.getElementById('signInRegisterButton'); |
| const profileControls = document.getElementById('profileControls'); |
| |
| |
| const profileAvatarLarge = document.getElementById('profileAvatarLarge'); |
| const profileUsernameDisplay = document.getElementById('profileUsernameDisplay'); |
| const profileEmailDisplay = document.getElementById('profileEmailDisplay'); |
| const accountName = document.getElementById('accountName'); |
| const accountEmail = document.getElementById('accountEmail'); |
| const chatHistoryList = document.getElementById('chatHistoryList'); |
| const profileSettingsButton = document.getElementById('profileSettingsButton'); |
| const profileLogoutButton = document.getElementById('profileLogoutButton'); |
| const profileLink = document.getElementById('profileLink'); |
| const settingsLink = document.getElementById('settingsLink'); |
| const emptyChatHistoryMessage = document.getElementById('emptyChatHistoryMessage'); |
| |
| |
| if (localStorage.getItem('isLoggedIn') !== 'true') { |
| window.location.href = '/auth'; |
| } |
| |
| |
| const user = JSON.parse(localStorage.getItem('user') || '{}'); |
| if (user.name) { |
| profileUsernameDisplay.textContent = user.name; |
| accountName.textContent = user.name; |
| } |
| if (user.email) { |
| profileEmailDisplay.textContent = user.email; |
| accountEmail.textContent = user.email; |
| } |
| |
| |
| |
| let avatarUpload = document.getElementById('avatarUpload'); |
| const editUsernameIcon = document.getElementById('editUsernameIcon'); |
| const usernameInputContainer = document.getElementById('usernameInputContainer'); |
| const usernameEditInput = document.getElementById('usernameEditInput'); |
| const usernameSaveButton = document.getElementById('usernameSaveButton'); |
| |
| |
| const profileAboutMeDisplay = document.getElementById('profileAboutMeDisplay'); |
| const editAboutMeIcon = document.getElementById('editAboutMeIcon'); |
| const aboutMeInputContainer = document.getElementById('aboutMeInputContainer'); |
| const aboutMeEditInput = document.getElementById('aboutMeEditInput'); |
| const aboutMeSaveButton = document.getElementById('aboutMeSaveButton'); |
| |
| |
| const chatHistorySearchInput = document.getElementById('chatHistorySearchInput'); |
| |
| |
| const chatContextMenu = document.getElementById('chatContextMenu'); |
| let currentChatContextItem = null; |
| |
| |
| const toastContainer = document.getElementById('toastContainer'); |
| |
| |
| confirmModal = document.getElementById('confirmModal'); |
| confirmTitle = document.getElementById('confirmTitle'); |
| confirmMessage = document.getElementById('confirmMessage'); |
| confirmOkButton = document.getElementById('confirmOkButton'); |
| confirmCancelButton = document.getElementById('confirmCancelButton'); |
| |
| const promptModal = document.getElementById('promptModal'); |
| const promptTitle = document.getElementById('promptTitle'); |
| const promptMessage = document.getElementById('promptMessage'); |
| const promptInput = document.getElementById('promptInput'); |
| const promptSubmitButton = document.getElementById('promptSubmitButton'); |
| const promptCancelButton = document.getElementById('promptCancelButton'); |
| |
| |
| const tagModal = document.getElementById('tagModal'); |
| const tagModalTitle = document.getElementById('tagModalTitle'); |
| const tagOptions = document.getElementById('tagOptions'); |
| const tagSaveButton = document.getElementById('tagSaveButton'); |
| const tagCancelButton = document.getElementById('tagCancelButton'); |
| let currentChatIdForTagging = null; |
| let selectedTags = new Set(); |
| |
| |
| const startChattingButton = document.getElementById('startChattingButton'); |
| |
| |
| |
| |
| let mockUserData = JSON.parse(sessionStorage.getItem('userData')) || { |
| name: 'GAKR User', |
| email: 'gakr.user@example.com', |
| avatar: null, |
| aboutMe: '', |
| pinnedChats: [] |
| }; |
| |
| |
| if (mockUserData.pinnedChats && Array.isArray(mockUserData.pinnedChats)) { |
| mockUserData.pinnedChats = new Set(mockUserData.pinnedChats); |
| } else if (!mockUserData.pinnedChats) { |
| mockUserData.pinnedChats = new Set(); |
| } |
| |
| let mockChatHistory = JSON.parse(sessionStorage.getItem('mockChatHistory')) || [ |
| { id: 1, title: "What is AI?", date: "2 days ago", tags: ['Learning'], lastActive: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000).toISOString() }, |
| { id: 2, title: "Poem about the ocean", date: "Yesterday", tags: ['Personal', 'Creative'], lastActive: new Date(Date.now() - 1 * 24 * 60 * 60 * 1000).toISOString() }, |
| { id: 3, title: "Explain quantum physics to me simply", date: "Today", tags: ['Learning', 'Ideas'], lastActive: new Date().toISOString() }, |
| { 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() }, |
| { id: 5, title: "History of the internet", date: "Last week", tags: ['Learning', 'Work'], lastActive: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString() } |
| ]; |
| |
| |
| mockChatHistory = mockChatHistory.map(chat => ({ |
| ...chat, |
| tags: new Set(chat.tags || []) |
| })); |
| |
| |
| |
| |
| |
| function showToast(message, type = 'info', duration = 3000) { |
| const toast = document.createElement('div'); |
| toast.className = `gakr-toast ${type}`; |
| let iconClass = ''; |
| switch(type) { |
| case 'success': iconClass = 'fas fa-check-circle'; break; |
| case 'error': iconClass = 'fas fa-times-circle'; break; |
| case 'warning': iconClass = 'fas fa-exclamation-triangle'; break; |
| default: iconClass = 'fas fa-info-circle'; break; |
| } |
| toast.innerHTML = `<i class="${iconClass} toast-icon"></i> <span>${message}</span>`; |
| toastContainer.appendChild(toast); |
| |
| |
| setTimeout(() => { |
| toast.classList.add('show'); |
| }, 10); |
| |
| |
| setTimeout(() => { |
| toast.classList.remove('show'); |
| toast.classList.add('hide'); |
| toast.addEventListener('transitionend', () => toast.remove(), { once: true }); |
| }, duration); |
| } |
| |
| |
| function showPromptDialog(message, defaultValue = '', title = 'Enter Information') { |
| return new Promise((resolve) => { |
| promptTitle.textContent = title; |
| promptMessage.textContent = message; |
| promptInput.value = defaultValue; |
| promptModal.classList.add('show'); |
| promptInput.focus(); |
| |
| const triggeringElement = document.activeElement; |
| |
| const handleSubmit = () => { |
| promptModal.classList.remove('show'); |
| promptSubmitButton.removeEventListener('click', handleSubmit); |
| promptCancelButton.removeEventListener('click', handleCancel); |
| promptInput.removeEventListener('keypress', handleKeyPress); |
| if (triggeringElement) triggeringElement.focus(); |
| resolve(promptInput.value); |
| }; |
| |
| const handleCancel = () => { |
| promptModal.classList.remove('show'); |
| promptSubmitButton.removeEventListener('click', handleSubmit); |
| promptCancelButton.removeEventListener('click', handleCancel); |
| promptInput.removeEventListener('keypress', handleKeyPress); |
| if (triggeringElement) triggeringElement.focus(); |
| resolve(null); |
| }; |
| |
| const handleKeyPress = (event) => { |
| if (event.key === 'Enter') { |
| handleSubmit(); |
| } |
| }; |
| |
| promptSubmitButton.addEventListener('click', handleSubmit); |
| promptCancelButton.addEventListener('click', handleCancel); |
| promptInput.addEventListener('keypress', handleKeyPress); |
| |
| |
| promptModal.addEventListener('click', function(event) { |
| if (event.target === promptModal) { |
| handleCancel(); |
| } |
| }, { once: true }); |
| }); |
| } |
| |
| |
| function getUserData() { |
| return mockUserData; |
| } |
| |
| function setUserData(data) { |
| |
| mockUserData = { ...mockUserData, ...data }; |
| |
| const dataToSave = { |
| ...mockUserData, |
| pinnedChats: Array.from(mockUserData.pinnedChats) |
| }; |
| sessionStorage.setItem('userData', JSON.stringify(dataToSave)); |
| updateProfileUI(); |
| } |
| |
| function getChatHistory() { |
| |
| return JSON.parse(JSON.stringify(Array.from(mockChatHistory).map(chat => ({ ...chat, tags: Array.from(chat.tags) })))); |
| } |
| |
| function saveChatHistory() { |
| |
| const chatsToSave = mockChatHistory.map(chat => ({ |
| ...chat, |
| tags: Array.from(chat.tags) |
| })); |
| sessionStorage.setItem('mockChatHistory', JSON.stringify(chatsToSave)); |
| } |
| |
| function updateChatInHistory(chatId, update) { |
| const index = mockChatHistory.findIndex(chat => chat.id === chatId); |
| if (index !== -1) { |
| |
| if (update.tags && ! (update.tags instanceof Set)) { |
| update.tags = new Set(update.tags); |
| } |
| mockChatHistory[index] = { ...mockChatHistory[index], ...update }; |
| saveChatHistory(); |
| } |
| } |
| |
| function deleteChatFromHistory(chatId) { |
| const initialLength = mockChatHistory.length; |
| mockChatHistory = mockChatHistory.filter(chat => chat.id !== chatId); |
| if (mockUserData.pinnedChats.has(chatId)) { |
| mockUserData.pinnedChats.delete(chatId); |
| setUserData({}); |
| } else { |
| saveChatHistory(); |
| } |
| return mockChatHistory.length < initialLength; |
| } |
| |
| |
| function updateProfileUI() { |
| const userData = getUserData(); |
| |
| |
| if (profileButton) { |
| profileButton.textContent = userData.name.charAt(0).toUpperCase(); |
| profileButton.title = userData.name; |
| } |
| |
| |
| if (profileAvatarLarge) { |
| profileAvatarLarge.innerHTML = ''; |
| if (userData.avatar) { |
| const img = document.createElement('img'); |
| img.src = userData.avatar; |
| img.alt = "User Avatar"; |
| profileAvatarLarge.appendChild(img); |
| } else { |
| profileAvatarLarge.textContent = userData.name.charAt(0).toUpperCase(); |
| } |
| |
| const label = document.createElement('label'); |
| label.htmlFor = 'avatarUpload'; |
| label.className = 'profile-avatar-edit-icon'; |
| label.title = 'Change Avatar'; |
| label.innerHTML = '<i class="fas fa-camera"></i>'; |
| |
| const newInput = document.createElement('input'); |
| newInput.type = 'file'; |
| newInput.id = 'avatarUpload'; |
| newInput.accept = 'image/*'; |
| newInput.style.display = 'none'; |
| label.appendChild(newInput); |
| profileAvatarLarge.appendChild(label); |
| |
| |
| avatarUpload = newInput; |
| |
| avatarUpload.addEventListener('change', handleAvatarUpload); |
| } |
| |
| |
| if (profileUsernameDisplay) profileUsernameDisplay.textContent = userData.name; |
| if (accountName) accountName.textContent = userData.name; |
| if (profileEmailDisplay) profileEmailDisplay.textContent = userData.email; |
| if (accountEmail) accountEmail.textContent = userData.email; |
| |
| |
| if (profileAboutMeDisplay) { |
| profileAboutMeDisplay.textContent = userData.aboutMe || 'Tell us something about yourself...'; |
| |
| profileAboutMeDisplay.classList.toggle('placeholder', !userData.aboutMe); |
| } |
| |
| |
| |
| if (signInRegisterButton) signInRegisterButton.style.display = 'none'; |
| if (profileControls) profileControls.style.display = 'block'; |
| |
| |
| renderChatHistory(chatHistorySearchInput.value); |
| } |
| |
| |
| function handleAvatarUpload(event) { |
| const file = event.target.files[0]; |
| if (file) { |
| const reader = new FileReader(); |
| reader.onload = function(e) { |
| setUserData({ avatar: e.target.result }); |
| showToast('Avatar updated successfully!', 'success'); |
| }; |
| reader.onerror = function() { |
| showToast('Failed to read file.', 'error'); |
| }; |
| reader.readAsDataURL(file); |
| } else { |
| showToast('No file selected.', 'warning'); |
| } |
| } |
| |
| function toggleUsernameEdit(show) { |
| if (show) { |
| profileUsernameDisplay.style.display = 'none'; |
| editUsernameIcon.style.display = 'none'; |
| usernameInputContainer.style.display = 'flex'; |
| usernameEditInput.value = getUserData().name; |
| usernameEditInput.focus(); |
| usernameEditInput.setSelectionRange(usernameEditInput.value.length, usernameEditInput.value.length); |
| } else { |
| profileUsernameDisplay.style.display = 'block'; |
| editUsernameIcon.style.display = 'inline-block'; |
| usernameInputContainer.style.display = 'none'; |
| } |
| } |
| |
| function toggleAboutMeEdit(show) { |
| if (show) { |
| profileAboutMeDisplay.style.display = 'none'; |
| editAboutMeIcon.style.display = 'none'; |
| aboutMeInputContainer.style.display = 'flex'; |
| aboutMeEditInput.value = getUserData().aboutMe; |
| aboutMeEditInput.focus(); |
| aboutMeEditInput.setSelectionRange(aboutMeEditInput.value.length, aboutMeEditInput.value.length); |
| } else { |
| profileAboutMeDisplay.style.display = 'block'; |
| editAboutMeIcon.style.display = 'inline-block'; |
| aboutMeInputContainer.style.display = 'none'; |
| } |
| } |
| |
| |
| |
| function renderChatHistory(searchTerm = '') { |
| const chatHistory = getChatHistory(); |
| chatHistoryList.innerHTML = ''; |
| |
| |
| let filteredChats = chatHistory.filter(chat => |
| chat.title.toLowerCase().includes(searchTerm.toLowerCase()) || |
| Array.from(chat.tags).some(tag => tag.toLowerCase().includes(searchTerm.toLowerCase())) |
| ); |
| |
| |
| let pinnedChats = []; |
| let unpinnedChats = []; |
| |
| filteredChats.forEach(chat => { |
| |
| if (mockUserData.pinnedChats.has(chat.id)) { |
| pinnedChats.push(chat); |
| } else { |
| unpinnedChats.push(chat); |
| } |
| }); |
| |
| |
| const sortFunction = (a, b) => { |
| const dateA = new Date(a.lastActive); |
| const dateB = new Date(b.lastActive); |
| return dateB.getTime() - dateA.getTime(); |
| }; |
| |
| pinnedChats.sort(sortFunction); |
| unpinnedChats.sort(sortFunction); |
| |
| |
| const chatsToDisplay = [...pinnedChats, ...unpinnedChats]; |
| |
| if (chatsToDisplay.length === 0) { |
| emptyChatHistoryMessage.style.display = 'block'; |
| } else { |
| emptyChatHistoryMessage.style.display = 'none'; |
| chatsToDisplay.forEach(chat => { |
| const chatItem = document.createElement('div'); |
| chatItem.className = 'chat-history-item'; |
| chatItem.dataset.chatId = chat.id; |
| if (mockUserData.pinnedChats.has(chat.id)) { |
| chatItem.classList.add('pinned'); |
| } |
| |
| |
| let lastActiveText = ''; |
| try { |
| const lastActiveDate = new Date(chat.lastActive); |
| const now = new Date(); |
| const diffMs = now.getTime() - lastActiveDate.getTime(); |
| const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24)); |
| |
| if (diffDays === 0 && lastActiveDate.toDateString() === now.toDateString()) { |
| lastActiveText = `Today, ${lastActiveDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`; |
| } else if (diffDays === 1 || (diffDays === 0 && lastActiveDate.getDate() === now.getDate() - 1)) { |
| lastActiveText = `Yesterday, ${lastActiveDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`; |
| } else if (diffDays < 7) { |
| lastActiveText = `${lastActiveDate.toLocaleDateString('en-US', { weekday: 'short' })}, ${lastActiveDate.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}`; |
| } else { |
| lastActiveText = lastActiveDate.toLocaleDateString(); |
| } |
| } catch (e) { |
| lastActiveText = chat.date; |
| } |
| |
| |
| chatItem.innerHTML = ` |
| <div class="chat-title-wrapper"> |
| <span class="chat-title">${chat.title}</span> |
| ${mockUserData.pinnedChats.has(chat.id) ? '<i class="fas fa-thumbtack pinned-icon" title="Pinned"></i>' : ''} |
| </div> |
| <div class="chat-meta"> |
| <span class="chat-date">${chat.date}</span> |
| <span class="chat-history-last-active">Last active: ${lastActiveText}</span> |
| </div> |
| <div class="chat-tags"> |
| ${Array.from(chat.tags).map(tag => `<span>${tag}</span>`).join('')} |
| </div> |
| `; |
| chatHistoryList.appendChild(chatItem); |
| }); |
| } |
| } |
| |
| |
| function showContextMenu(x, y, chatItem) { |
| |
| let posX = x; |
| let posY = y; |
| |
| |
| const menuWidth = chatContextMenu.offsetWidth; |
| if (posX + menuWidth > window.innerWidth - 20) { |
| posX = window.innerWidth - menuWidth - 20; |
| } |
| |
| const menuHeight = chatContextMenu.offsetHeight; |
| if (posY + menuHeight > window.innerHeight - 20) { |
| posY = window.innerHeight - menuHeight - 20; |
| } |
| |
| chatContextMenu.style.left = `${posX}px`; |
| chatContextMenu.style.top = `${posY}px`; |
| chatContextMenu.classList.add('show'); |
| currentChatContextItem = chatItem; |
| |
| const chatId = parseInt(currentChatContextItem.dataset.chatId); |
| const pinUnpinLink = chatContextMenu.querySelector('[data-action="pin"], [data-action="unpin"]'); |
| if (mockUserData.pinnedChats.has(chatId)) { |
| pinUnpinLink.innerHTML = '<i class="fas fa-thumbtack"></i> Unpin'; |
| pinUnpinLink.dataset.action = 'unpin'; |
| } else { |
| pinUnpinLink.innerHTML = '<i class="fas fa-thumbtack"></i> Pin'; |
| pinUnpinLink.dataset.action = 'pin'; |
| } |
| } |
| |
| function hideContextMenu() { |
| chatContextMenu.classList.remove('show'); |
| currentChatContextItem = null; |
| } |
| |
| |
| function showTagModal(chatId, currentTags) { |
| currentChatIdForTagging = chatId; |
| selectedTags = new Set(currentTags); |
| |
| |
| tagOptions.innerHTML = ''; |
| |
| const availableTags = ['Work', 'Personal', 'Creative', 'Ideas', 'Learning', 'Research', 'Drafts', 'Notes', 'Projects', 'Finance', 'Health']; |
| |
| availableTags.forEach(tag => { |
| const tagDiv = document.createElement('div'); |
| tagDiv.className = 'tag-option'; |
| tagDiv.dataset.tag = tag; |
| tagDiv.textContent = tag; |
| if (selectedTags.has(tag)) { |
| tagDiv.classList.add('selected'); |
| } |
| tagDiv.addEventListener('click', () => { |
| if (selectedTags.has(tag)) { |
| selectedTags.delete(tag); |
| tagDiv.classList.remove('selected'); |
| } else { |
| selectedTags.add(tag); |
| tagDiv.classList.add('selected'); |
| } |
| }); |
| tagOptions.appendChild(tagDiv); |
| }); |
| |
| tagModal.classList.add('show'); |
| |
| tagModal.triggeringElement = document.activeElement; |
| } |
| |
| function hideTagModal() { |
| tagModal.classList.remove('show'); |
| currentChatIdForTagging = null; |
| selectedTags = new Set(); |
| if (tagModal.triggeringElement) tagModal.triggeringElement.focus(); |
| tagModal.triggeringElement = null; |
| } |
| |
| |
| |
| |
| |
| profileButton.addEventListener('click', function(event) { |
| event.stopPropagation(); |
| profileDropdown.classList.toggle('show'); |
| }); |
| |
| |
| document.addEventListener('click', function(event) { |
| if (!profileDropdown.contains(event.target) && !profileButton.contains(event.target)) { |
| profileDropdown.classList.remove('show'); |
| } |
| if (!chatContextMenu.contains(event.target) && !event.target.closest('.chat-history-item')) { |
| hideContextMenu(); |
| } |
| }); |
| |
| |
| chatContextMenu.addEventListener('contextmenu', function(event) { |
| event.preventDefault(); |
| }); |
| |
| |
| if (profileLink) { |
| profileLink.addEventListener('click', (e) => { |
| e.preventDefault(); |
| profileDropdown.classList.remove('show'); |
| showToast('You are already on your profile page!', 'info'); |
| }); |
| } |
| if (profileSettingsButton) { |
| profileSettingsButton.addEventListener('click', function(e) { |
| e.preventDefault(); |
| showToast('Profile settings coming soon!', 'info'); |
| }); |
| } |
| if (settingsLink) { |
| settingsLink.addEventListener('click', (e) => { |
| e.preventDefault(); |
| showToast('Settings page is under development!', 'info'); |
| profileDropdown.classList.remove('show'); |
| }); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| editUsernameIcon.addEventListener('click', () => toggleUsernameEdit(true)); |
| usernameSaveButton.addEventListener('click', function() { |
| const newUsername = usernameEditInput.value.trim(); |
| if (newUsername) { |
| setUserData({ name: newUsername }); |
| toggleUsernameEdit(false); |
| showToast('Username updated successfully!', 'success'); |
| } else { |
| showToast('Username cannot be empty!', 'error'); |
| } |
| }); |
| usernameEditInput.addEventListener('keypress', function(e) { |
| if (e.key === 'Enter') { |
| usernameSaveButton.click(); |
| } |
| }); |
| |
| |
| editAboutMeIcon.addEventListener('click', () => toggleAboutMeEdit(true)); |
| aboutMeSaveButton.addEventListener('click', function() { |
| const newAboutMe = aboutMeEditInput.value.trim(); |
| setUserData({ aboutMe: newAboutMe }); |
| toggleAboutMeEdit(false); |
| showToast('About Me section updated!', 'success'); |
| }); |
| aboutMeEditInput.addEventListener('keypress', function(e) { |
| if (e.key === 'Enter' && !e.shiftKey) { |
| e.preventDefault(); |
| aboutMeSaveButton.click(); |
| } |
| }); |
| |
| |
| |
| chatHistorySearchInput.addEventListener('input', () => renderChatHistory(chatHistorySearchInput.value)); |
| |
| |
| |
| chatHistoryList.addEventListener('contextmenu', function(event) { |
| const chatItem = event.target.closest('.chat-history-item'); |
| if (chatItem) { |
| event.preventDefault(); |
| showContextMenu(event.clientX, event.clientY, chatItem); |
| } |
| }); |
| |
| |
| chatContextMenu.addEventListener('click', function(event) { |
| event.preventDefault(); |
| const actionElement = event.target.closest('.chat-context-menu-item'); |
| if (actionElement && currentChatContextItem) { |
| const action = actionElement.dataset.action; |
| const chatId = parseInt(currentChatContextItem.dataset.chatId); |
| |
| switch (action) { |
| case 'delete': |
| showConfirmDialog('Are you sure you want to delete this conversation? This action cannot be undone.', 'Delete Conversation') |
| .then(result => { |
| if (result) { |
| if (deleteChatFromHistory(chatId)) { |
| showToast('Conversation deleted.', 'success'); |
| renderChatHistory(chatHistorySearchInput.value); |
| } else { |
| showToast('Failed to delete conversation.', 'error'); |
| } |
| } |
| }); |
| break; |
| case 'share': |
| showPromptDialog('Enter email or username to share with:', '', 'Share Conversation') |
| .then(input => { |
| if (input !== null) { |
| if (input.trim() !== '') { |
| showToast(`Conversation shared with "${input}". (Mock)`, 'success'); |
| } else { |
| showToast('Share cancelled. No recipient provided.', 'warning'); |
| } |
| } |
| }); |
| break; |
| case 'pin': |
| mockUserData.pinnedChats.add(chatId); |
| setUserData({}); |
| showToast('Conversation pinned!', 'success'); |
| |
| break; |
| case 'unpin': |
| mockUserData.pinnedChats.delete(chatId); |
| setUserData({}); |
| showToast('Conversation unpinned!', 'info'); |
| |
| break; |
| case 'tag': |
| const chatToTag = mockChatHistory.find(chat => chat.id === chatId); |
| if (chatToTag) { |
| showTagModal(chatId, chatToTag.tags); |
| } |
| break; |
| } |
| hideContextMenu(); |
| } |
| }); |
| |
| |
| chatHistoryList.addEventListener('click', function(event) { |
| const chatItem = event.target.closest('.chat-history-item'); |
| if (chatItem) { |
| const chatId = chatItem.dataset.chatId; |
| showToast(`Opening conversation: ${chatItem.querySelector('.chat-title').textContent} (ID: ${chatId}). (Mock)`, 'info'); |
| |
| |
| } |
| }); |
| |
| |
| tagSaveButton.addEventListener('click', function() { |
| if (currentChatIdForTagging !== null) { |
| |
| updateChatInHistory(currentChatIdForTagging, { tags: Array.from(selectedTags) }); |
| showToast('Tags updated successfully!', 'success'); |
| hideTagModal(); |
| renderChatHistory(chatHistorySearchInput.value); |
| } |
| }); |
| |
| tagCancelButton.addEventListener('click', hideTagModal); |
| |
| |
| tagModal.addEventListener('click', function(event) { |
| if (event.target === tagModal) { |
| hideTagModal(); |
| } |
| }); |
| |
| |
| updateProfileUI(); |
| }); |
| </script> |
| </body> |
| </html> |
|
|