| | <!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 - Chat</title> |
| | <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"> |
| | <link rel="stylesheet" href="style.css"> |
| | <style> |
| | |
| | :root { |
| | |
| | --gakr-blue: #4285F4; |
| | --gakr-blue-dark: #1a73e8; |
| | --gakr-blue-light: #e8f0fe; |
| | --gakr-grey-text: #5f6368; |
| | --gakr-grey-hover-bg: rgba(95, 99, 104, 0.1); |
| | } |
| | |
| | :root[data-bs-theme="dark"] { |
| | |
| | --gakr-blue: #8ab4f8; |
| | --gakr-blue-dark: #669df6; |
| | --gakr-blue-light: rgba(138, 180, 248, 0.1); |
| | --gakr-grey-text: #bdc1c6; |
| | --gakr-grey-hover-bg: rgba(189, 193, 198, 0.1); |
| | |
| | } |
| | |
| | |
| | .gemini-chat-layout { |
| | display: flex; |
| | flex-direction: column; |
| | height: 100vh; |
| | width: 100%; |
| | background-color: var(--bs-body-bg); |
| | color: var(--bs-body-color); |
| | } |
| | |
| | .gemini-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; |
| | } |
| | |
| | .gemini-logo-area { |
| | display: flex; |
| | align-items: center; |
| | gap: 0.75rem; |
| | } |
| | |
| | .gemini-brand-logo { |
| | width: 24px; |
| | height: 24px; |
| | display: flex; |
| | align-items: center; |
| | justify-content: center; |
| | } |
| | |
| | .gemini-brand-text { |
| | font-size: 1.25rem; |
| | font-weight: 500; |
| | color: var(--gakr-blue); |
| | } |
| | |
| | .gemini-chat-wrapper { |
| | flex: 1; |
| | overflow: hidden; |
| | position: relative; |
| | display: flex; |
| | flex-direction: column; |
| | } |
| | |
| | .gemini-chat-container { |
| | flex: 1; |
| | overflow-y: auto; |
| | padding: 1rem; |
| | display: flex; |
| | flex-direction: column; |
| | gap: 1.5rem; |
| | width: 100%; |
| | max-width: 800px; |
| | margin: 0 auto; |
| | } |
| | |
| | .gemini-prompt-area { |
| | padding: 1rem; |
| | border-top: 1px solid var(--bs-border-color); |
| | width: 100%; |
| | max-width: 800px; |
| | margin: 0 auto; |
| | |
| | position: relative; |
| | |
| | } |
| | |
| | .gemini-message { |
| | display: flex; |
| | flex-direction: column; |
| | gap: 0.5rem; |
| | max-width: 90%; |
| | } |
| | |
| | .gemini-message-user { |
| | align-self: flex-end; |
| | } |
| | |
| | .gemini-message-ai { |
| | align-self: flex-start; |
| | } |
| | |
| | .gemini-message-header { |
| | display: flex; |
| | align-items: center; |
| | gap: 0.5rem; |
| | font-size: 0.85rem; |
| | color: var(--bs-secondary-color); |
| | } |
| | |
| | .gemini-message-avatar { |
| | width: 24px; |
| | height: 24px; |
| | border-radius: 50%; |
| | display: flex; |
| | align-items: center; |
| | justify-content: center; |
| | font-size: 0.75rem; |
| | } |
| | |
| | .gemini-message-content { |
| | padding: 1rem; |
| | border-radius: 12px; |
| | line-height: 1.5; |
| | |
| | white-space: pre-wrap; |
| | word-break: break-word; |
| | } |
| | |
| | .gemini-message-user .gemini-message-content { |
| | background-color: var(--bs-tertiary-bg); |
| | border-top-right-radius: 4px; |
| | } |
| | |
| | .gemini-message-ai .gemini-message-content { |
| | background-color: rgba(66, 133, 244, 0.1); |
| | border-top-left-radius: 4px; |
| | } |
| | |
| | .gemini-message-user .gemini-message-avatar { |
| | background-color: var(--bs-tertiary-bg); |
| | } |
| | |
| | .gemini-message-ai .gemini-message-avatar { |
| | background-color: rgba(66, 133, 244, 0.2); |
| | color: var(--gakr-blue); |
| | } |
| | |
| | |
| | .gemini-input-container { |
| | position: relative; |
| | border-radius: 24px; |
| | border: 1px solid var(--bs-border-color); |
| | background: var(--bs-body-bg); |
| | padding: 0.75rem 1rem; |
| | display: flex; |
| | |
| | align-items: flex-end; |
| | |
| | box-shadow: 0 1px 5px rgba(0,0,0,0.1); |
| | |
| | transition: border-color 0.3s ease-in-out, box-shadow 0.3s ease-in-out; |
| | flex-wrap: wrap; |
| | gap: 0.5rem; |
| | } |
| | |
| | |
| | .gemini-input-container:focus-within { |
| | border-color: var(--gakr-blue); |
| | box-shadow: 0 1px 8px rgba(66, 133, 244, 0.2); |
| | background: var(--bs-body-bg); |
| | } |
| | |
| | |
| | .gemini-input { |
| | flex: 1; |
| | border: none; |
| | background: transparent; |
| | |
| | padding: 0; |
| | |
| | |
| | outline: none; |
| | color: var(--bs-body-color); |
| | font-size: 1rem; |
| | resize: none; |
| | overflow-y: hidden; |
| | line-height: 1.4; |
| | |
| | min-height: calc(1rem * 1.4); |
| | max-height: calc(1rem * 1.4 * 5); |
| | box-sizing: border-box; |
| | white-space: pre-wrap; |
| | word-break: break-word; |
| | vertical-align: bottom; |
| | transition: height 0.3s ease-in-out; |
| | } |
| | |
| | .gemini-input:focus { |
| | outline: none; |
| | } |
| | |
| | |
| | .gemini-input-actions { |
| | display: flex; |
| | |
| | align-items: flex-end; |
| | |
| | padding-bottom: 0px; |
| | |
| | gap: 0.5rem; |
| | font-size: 1.25rem; |
| | flex-shrink: 0; |
| | } |
| | |
| | |
| | .gemini-action-button { |
| | width: 36px; |
| | height: 36px; |
| | border-radius: 50%; |
| | display: flex; |
| | align-items: center; |
| | justify-content: center; |
| | cursor: pointer; |
| | color: var(--bs-secondary-color); |
| | |
| | transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out; |
| | } |
| | |
| | .gemini-action-button:hover { |
| | background-color: var(--bs-tertiary-bg); |
| | color: var(--bs-body-color); |
| | } |
| | |
| | .gemini-action-button:active { |
| | transform: scale(0.95); |
| | background-color: var(--bs-tertiary-bg); |
| | color: var(--bs-body-color); |
| | transition: background-color 0s, transform 0.1s; |
| | } |
| | |
| | .gemini-submit-button { |
| | color: var(--gakr-blue); |
| | } |
| | |
| | |
| | .gemini-submit-button:hover { |
| | background-color: var(--gakr-blue-light); |
| | color: var(--gakr-blue-dark); |
| | } |
| | |
| | .gemini-submit-button:active { |
| | transform: scale(0.95); |
| | background-color: var(--gakr-blue-light); |
| | color: var(--gakr-blue-dark); |
| | transition: background-color 0s, transform 0.1s; |
| | } |
| | |
| | .gemini-submit-button.disabled { |
| | opacity: 0.5; |
| | cursor: default; |
| | |
| | transition: none; |
| | } |
| | |
| | .gemini-submit-button.disabled:hover { |
| | background-color: transparent; |
| | color: var(--gakr-blue); |
| | } |
| | |
| | |
| | |
| | .gemini-typing { |
| | display: inline-flex; |
| | align-items: center; |
| | gap: 4px; |
| | padding: 0.75rem 1rem; |
| | border-radius: 12px; |
| | background-color: rgba(66, 133, 244, 0.1); |
| | margin-bottom: 1rem; |
| | align-self: flex-end; |
| | } |
| | |
| | .gemini-typing-dot { |
| | width: 6px; |
| | height: 6px; |
| | border-radius: 50%; |
| | background-color: var(--gakr-blue); |
| | animation: typing 1.3s infinite ease-in-out; |
| | } |
| | |
| | .gemini-typing-dot:nth-child(1) { animation-delay: 0s; } |
| | .gemini-typing-dot:nth-child(2) { animation-delay: 0.2s; } |
| | .gemini-typing-dot:nth-child(3) { animation-delay: 0.4s; } |
| | |
| | .gemini-thinking-toggle { |
| | background: none; |
| | border: none; |
| | color: var(--gakr-blue); |
| | cursor: pointer; |
| | font-size: 0.9rem; |
| | padding: 0; |
| | margin-bottom: 0.5rem; |
| | text-decoration: underline; |
| | } |
| | |
| | .gemini-thinking-content { |
| | display: block; |
| | margin-top: 0.5rem; |
| | padding: 0.5rem; |
| | background-color: rgba(138, 180, 248, 0.05); |
| | border-left: 3px solid var(--gakr-blue); |
| | font-style: italic; |
| | } |
| | |
| | .gemini-welcome { |
| | text-align: center; |
| | max-width: 600px; |
| | margin: 4rem auto; |
| | } |
| | |
| | .gemini-welcome-icon { |
| | font-size: 3rem; |
| | margin-bottom: 1.5rem; |
| | color: var(--gakr-blue); |
| | } |
| | |
| | .gemini-welcome-title { |
| | font-size: 2rem; |
| | margin-bottom: 1rem; |
| | font-weight: 500; |
| | } |
| | |
| | |
| | .gemini-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; |
| | } |
| | |
| | .gemini-login-button:hover { |
| | background-color: rgba(66, 133, 244, 0.1); |
| | } |
| | |
| | |
| | .gemini-login-prompt { |
| | position: fixed; |
| | top: 0; |
| | left: 0; |
| | width: 100%; |
| | height: 100%; |
| | background-color: rgba(0, 0, 0, 0.5); |
| | display: flex; |
| | align-items: center; |
| | justify-content: center; |
| | z-index: 1000; |
| | opacity: 0; |
| | visibility: hidden; |
| | transition: opacity 0.3s, visibility 0.3s; |
| | } |
| | |
| | .gemini-login-prompt.show { |
| | opacity: 1; |
| | visibility: visible; |
| | } |
| | |
| | .gemini-login-prompt-content { |
| | background-color: var(--bs-body-bg); |
| | border-radius: 12px; |
| | padding: 1.5rem; |
| | max-width: 400px; |
| | width: 90%; |
| | } |
| | |
| | .gemini-login-prompt-title { |
| | font-size: 1.25rem; |
| | font-weight: 500; |
| | margin-bottom: 1rem; |
| | color: var(--gakr-blue); |
| | } |
| | |
| | .gemini-login-prompt-buttons { |
| | display: flex; |
| | flex-direction: column; |
| | gap: 0.75rem; |
| | margin-top: 1.5rem; |
| | } |
| | |
| | .gemini-button-primary { |
| | background-color: var(--gakr-blue); |
| | color: white; |
| | border: none; |
| | padding: 0.75rem 1rem; |
| | border-radius: 50px; |
| | font-size: 0.9rem; |
| | text-align: center; |
| | text-decoration: none; |
| | cursor: pointer; |
| | transition: background-color 0.2s; |
| | } |
| | |
| | .gemini-button-primary:hover { |
| | background-color: #3b78e7; |
| | } |
| | |
| | .gemini-button-secondary { |
| | background-color: transparent; |
| | color: var(--gakr-blue); |
| | border: 1px solid var(--gakr-blue); |
| | padding: 0.75rem 1rem; |
| | border-radius: 50px; |
| | font-size: 0.9rem; |
| | text-align: center; |
| | text-decoration: none; |
| | cursor: pointer; |
| | transition: background-color 0.2s; |
| | } |
| | |
| | .gemini-button-secondary:hover { |
| | background-color: rgba(66, 133, 244, 0.1); |
| | } |
| | |
| | .gemini-button-text { |
| | background-color: transparent; |
| | color: var(--bs-body-color); |
| | border: none; |
| | padding: 0.75rem 1rem; |
| | border-radius: 50px; |
| | font-size: 0.9rem; |
| | text-align: center; |
| | text-decoration: none; |
| | cursor: pointer; |
| | transition: background-color 0.2s; |
| | } |
| | |
| | .gemini-button-text:hover { |
| | background-color: var(--bs-tertiary-bg); |
| | } |
| | |
| | |
| | .attachment-options-container { |
| | position: absolute; |
| | bottom: 100%; |
| | left: 1rem; |
| | transform: translateY(-10px); |
| | z-index: 10; |
| | |
| | background-color: var(--bs-body-bg); |
| | border: 1px solid var(--bs-border-color); |
| | border-radius: 15px; |
| | box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); |
| | padding: 15px; |
| | |
| | display: flex; |
| | gap: 20px; |
| | justify-content: flex-start; |
| | min-width: max-content; |
| | |
| | opacity: 0; |
| | visibility: hidden; |
| | pointer-events: none; |
| | |
| | transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out, visibility 0s linear 0.3s; |
| | } |
| | |
| | |
| | .attachment-options-container.visible { |
| | opacity: 1; |
| | visibility: visible; |
| | pointer-events: auto; |
| | transform: translateY(-20px); |
| | transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out, visibility 0s linear 0s; |
| | } |
| | |
| | .attachment-option { |
| | display: flex; |
| | flex-direction: column; |
| | align-items: center; |
| | cursor: pointer; |
| | color: var(--bs-body-color); |
| | font-size: 0.8rem; |
| | gap: 5px; |
| | transition: color 0.2s ease-in-out; |
| | } |
| | |
| | .attachment-option:hover { |
| | color: var(--gakr-blue); |
| | } |
| | |
| | .attachment-icon { |
| | width: 48px; |
| | height: 48px; |
| | border-radius: 50%; |
| | background-color: var(--bs-tertiary-bg); |
| | display: flex; |
| | align-items: center; |
| | justify-content: center; |
| | font-size: 1.2rem; |
| | transition: background-color 0.2s ease-in-out; |
| | } |
| | |
| | .attachment-option:hover .attachment-icon { |
| | background-color: var(--gakr-blue-light); |
| | } |
| | |
| | .attachment-text { |
| | text-align: center; |
| | } |
| | |
| | |
| | .attached-file-preview-container { |
| | display: flex; |
| | flex-wrap: wrap; |
| | gap: 8px; |
| | |
| | width: 100%; |
| | } |
| | |
| | .attached-file-chip { |
| | display: flex; |
| | align-items: center; |
| | background-color: var(--bs-tertiary-bg); |
| | border-radius: 16px; |
| | padding: 4px 8px; |
| | font-size: 0.85rem; |
| | color: var(--bs-body-color); |
| | max-width: 150px; |
| | overflow: hidden; |
| | text-overflow: ellipsis; |
| | white-space: nowrap; |
| | position: relative; |
| | } |
| | |
| | .attached-file-chip .file-name { |
| | flex-grow: 1; |
| | overflow: hidden; |
| | text-overflow: ellipsis; |
| | white-space: nowrap; |
| | padding-right: 8px; |
| | } |
| | |
| | .attached-file-chip .remove-file { |
| | cursor: pointer; |
| | margin-left: 4px; |
| | color: var(--bs-secondary-color); |
| | } |
| | |
| | .attached-file-chip .remove-file:hover { |
| | color: var(--bs-body-color); |
| | } |
| | |
| | .attached-file-chip img { |
| | width: 24px; |
| | height: 24px; |
| | border-radius: 4px; |
| | object-fit: cover; |
| | margin-right: 8px; |
| | } |
| | |
| | |
| | .hidden-file-input { |
| | display: none; |
| | } |
| | |
| | |
| | |
| | |
| | </style> |
| | </head> |
| | <body> |
| | <div class="gemini-chat-layout"> |
| | <header class="gemini-chat-header"> |
| | <div class="gemini-logo-area"> |
| | <a href="/" class="gemini-brand-logo"> |
| | <i class="fas fa-robot" style="color: var(--gakr-blue);"></i> |
| | </a> |
| | <a href="/" class="gemini-brand-text" style="text-decoration: none;">GAKR AI</a> |
| | </div> |
| |
|
| | <div class="gemini-nav-controls"> |
| | <a href="auth.html" class="gemini-login-button">Sign in / Register</a> |
| | </div> |
| | </header> |
| |
|
| | <div class="gemini-chat-wrapper"> |
| | <div class="gemini-chat-container" id="chatContainer"> |
| | <div class="gemini-welcome" id="welcomeMessage"> |
| | <div class="gemini-welcome-icon"> |
| | <i class="fas fa-robot"></i> |
| | </div> |
| | <h1 class="gemini-welcome-title">How can I help you today?</h1> |
| | <p>I'm GAKR AI, your AI assistant. Ask me anything!</p> |
| | </div> |
| |
|
| | <div class="gemini-message gemini-message-ai d-none" id="initialMessage"> |
| | <div class="gemini-message-header"> |
| | <div class="gemini-message-avatar"> |
| | <i class="fas fa-robot"></i> |
| | </div> |
| | <span>GAKR AI</span> |
| | </div> |
| | <div class="gemini-message-content">Hello! I'm GAKR AI How can I help you today? |
| | </div> |
| | </div> |
| |
|
| | <div class="gemini-typing d-none" id="typingIndicator"> |
| | <div class="gemini-typing-dot"></div> |
| | <div class="gemini-typing-dot"></div> |
| | <div class="gemini-typing-dot"></div> |
| | </div> |
| | </div> |
| |
|
| | <div class="gemini-prompt-area"> |
| | <div class="gemini-input-container" id="inputContainer"> |
| | <div class="gemini-action-button" id="addButton"> |
| | <i class="fas fa-plus"></i> |
| | </div> |
| | <div class="attached-file-preview-container" id="attachedFilePreviewContainer"> |
| | </div> |
| | <textarea class="gemini-input" id="userInput" placeholder="Message GAKR AI..." rows="1"></textarea> |
| |
|
| | <div class="gemini-input-actions"> |
| | <div class="gemini-action-button"> |
| | <i class="fas fa-microphone"></i> |
| | </div> |
| | <div class="gemini-action-button gemini-submit-button disabled" id="submitButton"> |
| | <i class="fas fa-arrow-right"></i> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | <div class="attachment-options-container"> |
| | <div class="attachment-option" id="cameraOption"> |
| | <div class="attachment-icon"><i class="fas fa-camera"></i></div> |
| | <div class="attachment-text">Camera</div> |
| | <input type="file" id="cameraInput" class="hidden-file-input" accept="image/*" capture="camera"> |
| | </div> |
| | <div class="attachment-option" id="galleryOption"> |
| | <div class="attachment-icon"><i class="fas fa-image"></i></div> |
| | <div class="attachment-text">Gallery</div> |
| | <input type="file" id="galleryInput" class="hidden-file-input" accept="image/*"> |
| | </div> |
| | <div class="attachment-option" id="filesOption"> |
| | <div class="attachment-icon"><i class="fas fa-paperclip"></i></div> |
| | <div class="attachment-text">Files</div> |
| | <input type="file" id="filesInput" class="hidden-file-input" accept="*/*"> |
| | </div> |
| | <div class="attachment-option" id="driveOption"> |
| | <div class="attachment-icon"><i class="fab fa-google-drive"></i></div> |
| | <div class="attachment-text">Drive</div> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| |
|
| | <div class="gemini-login-prompt" id="loginPrompt"> |
| | <div class="gemini-login-prompt-content"> |
| | <div class="gemini-login-prompt-title">Continue with GAKR AI</div> |
| | <p>You've had 5 conversations with GAKR AI. Would you like to create an account to save your history?</p> |
| | <div class="gemini-login-prompt-buttons"> |
| | <a href="login.html" class="gemini-button-primary">Sign in</a> |
| | <a href="/login?signup=true" class="gemini-button-secondary">Create account</a> |
| | <button type="button" class="gemini-button-text" id="continueGuest">Continue as guest</button> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| | <script> |
| | document.addEventListener('DOMContentLoaded', function() { |
| | const chatContainer = document.getElementById('chatContainer'); |
| | const userInput = document.getElementById('userInput'); |
| | const submitButton = document.getElementById('submitButton'); |
| | const typingIndicator = document.getElementById('typingIndicator'); |
| | const welcomeMessage = document.getElementById('welcomeMessage'); |
| | const initialMessage = document.getElementById('initialMessage'); |
| | const loginPrompt = document.getElementById('loginPrompt'); |
| | const continueGuest = document.getElementById('continueGuest'); |
| | |
| | const addButton = document.getElementById('addButton'); |
| | const attachmentOptionsContainer = document.querySelector('.attachment-options-container'); |
| | |
| | const cameraInput = document.getElementById('cameraInput'); |
| | const galleryInput = document.getElementById('galleryInput'); |
| | const filesInput = document.getElementById('filesInput'); |
| | const attachedFilePreviewContainer = document.getElementById('attachedFilePreviewContainer'); |
| | const inputContainer = document.getElementById('inputContainer'); |
| | |
| | let messageCount = 0; |
| | let attachedFiles = []; |
| | |
| | |
| | const isLoggedIn = localStorage.getItem("gakr_is_logged_in") === "true"; |
| | |
| | |
| | const style = getComputedStyle(userInput); |
| | const lineHeight = parseFloat(style.lineHeight); |
| | const minHeight = lineHeight; |
| | const maxHeight = lineHeight * 5; |
| | |
| | const initialInputContainerPaddingTop = parseFloat(getComputedStyle(inputContainer).paddingTop); |
| | const initialInputContainerPaddingBottom = parseFloat(getComputedStyle(inputContainer).paddingBottom); |
| | const initialActionButtonsHeight = document.querySelector('.gemini-input-actions').offsetHeight; |
| | |
| | function autoGrowTextarea() { |
| | if (!userInput) { |
| | console.error("autoGrowTextarea called but userInput element not found."); |
| | return; |
| | } |
| | |
| | userInput.style.height = 'auto'; |
| | const scrollHeight = userInput.scrollHeight; |
| | |
| | let newTextareaHeight = Math.min(Math.max(scrollHeight, minHeight), maxHeight); |
| | userInput.style.height = newTextareaHeight + 'px'; |
| | |
| | if (attachedFiles.length > 0) { |
| | userInput.style.paddingTop = '0px'; |
| | userInput.style.paddingBottom = '0px'; |
| | inputContainer.style.alignItems = 'flex-start'; |
| | } else { |
| | userInput.style.paddingTop = '0px'; |
| | userInput.style.paddingBottom = '0px'; |
| | inputContainer.style.alignItems = 'flex-end'; |
| | } |
| | |
| | if (scrollHeight > maxHeight) { |
| | userInput.style.overflowY = 'auto'; |
| | } else { |
| | userInput.style.overflowY = 'hidden'; |
| | } |
| | |
| | |
| | if (userInput.value.trim().length > 0) { |
| | submitButton.classList.remove('disabled'); |
| | } else { |
| | submitButton.classList.add('disabled'); |
| | } |
| | } |
| | |
| | userInput.addEventListener('input', autoGrowTextarea); |
| | setTimeout(autoGrowTextarea, 0); |
| | |
| | |
| | |
| | |
| | userInput.addEventListener('keydown', function(event) { |
| | if (event.key === 'Enter' && !event.shiftKey && !submitButton.classList.contains('disabled')) { |
| | event.preventDefault(); |
| | sendMessage(); |
| | } |
| | }); |
| | |
| | |
| | submitButton.addEventListener('click', function() { |
| | if (!this.classList.contains('disabled')) { |
| | sendMessage(); |
| | } |
| | }); |
| | |
| | |
| | continueGuest.addEventListener('click', function() { |
| | loginPrompt.classList.remove('show'); |
| | }); |
| | |
| | |
| | addButton.addEventListener('click', function(event) { |
| | event.stopPropagation(); |
| | attachmentOptionsContainer.classList.toggle('visible'); |
| | }); |
| | |
| | |
| | document.getElementById('cameraOption').addEventListener('click', function() { |
| | cameraInput.click(); |
| | attachmentOptionsContainer.classList.remove('visible'); |
| | }); |
| | |
| | document.getElementById('galleryOption').addEventListener('click', function() { |
| | galleryInput.click(); |
| | attachmentOptionsContainer.classList.remove('visible'); |
| | }); |
| | |
| | document.getElementById('filesOption').addEventListener('click', function() { |
| | filesInput.click(); |
| | attachmentOptionsContainer.classList.remove('visible'); |
| | }); |
| | |
| | document.getElementById('driveOption').addEventListener('click', function() { |
| | alert("Google Drive integration requires backend setup and Google API access."); |
| | attachmentOptionsContainer.classList.remove('visible'); |
| | }); |
| | |
| | |
| | [cameraInput, galleryInput, filesInput].forEach(input => { |
| | input.addEventListener('change', function(event) { |
| | const files = event.target.files; |
| | if (files.length > 0) { |
| | for (let i = 0; i < files.length; i++) { |
| | attachedFiles.push(files[i]); |
| | addFileChip(files[i]); |
| | } |
| | autoGrowTextarea(); |
| | userInput.focus(); |
| | } |
| | event.target.value = ''; |
| | }); |
| | }); |
| | |
| | function addFileChip(file) { |
| | const chip = document.createElement('div'); |
| | chip.className = 'attached-file-chip'; |
| | chip.setAttribute('data-filename', file.name); |
| | |
| | let previewElement; |
| | if (file.type.startsWith('image/')) { |
| | const img = document.createElement('img'); |
| | img.src = URL.createObjectURL(file); |
| | img.alt = file.name; |
| | img.onload = () => URL.revokeObjectURL(img.src); |
| | previewElement = img; |
| | } else { |
| | const icon = document.createElement('i'); |
| | icon.className = 'fas fa-paperclip'; |
| | previewElement = icon; |
| | } |
| | |
| | const fileNameSpan = document.createElement('span'); |
| | fileNameSpan.className = 'file-name'; |
| | fileNameSpan.textContent = file.name; |
| | |
| | const removeButton = document.createElement('span'); |
| | removeButton.className = 'remove-file'; |
| | removeButton.innerHTML = '×'; |
| | removeButton.addEventListener('click', function() { |
| | removeFileChip(file.name); |
| | }); |
| | |
| | chip.appendChild(previewElement); |
| | chip.appendChild(fileNameSpan); |
| | chip.appendChild(removeButton); |
| | |
| | attachedFilePreviewContainer.appendChild(chip); |
| | autoGrowTextarea(); |
| | } |
| | |
| | function removeFileChip(filename) { |
| | const chipToRemove = attachedFilePreviewContainer.querySelector(`[data-filename="${filename}"]`); |
| | if (chipToRemove) { |
| | attachedFilePreviewContainer.removeChild(chipToRemove); |
| | attachedFiles = attachedFiles.filter(file => file.name !== filename); |
| | autoGrowTextarea(); |
| | } |
| | } |
| | |
| | |
| | document.addEventListener('click', function(event) { |
| | const isClickInsideAddButton = addButton && addButton.contains(event.target); |
| | const isClickInsideOptions = attachmentOptionsContainer && attachmentOptionsContainer.contains(event.target); |
| | |
| | if (attachmentOptionsContainer && attachmentOptionsContainer.classList.contains('visible') && !isClickInsideAddButton && !isClickInsideOptions) { |
| | attachmentOptionsContainer.classList.remove('visible'); |
| | } |
| | }); |
| | |
| | if (attachmentOptionsContainer) { |
| | attachmentOptionsContainer.addEventListener('click', function(event){ |
| | event.stopPropagation(); |
| | }); |
| | } |
| | |
| | |
| | let inlineLoginPromptShown = false; |
| | function maybeShowInlineLoginPrompt() { |
| | if (isLoggedIn) return; |
| | if (inlineLoginPromptShown) return; |
| | if (messageCount % 5 !== 0) return; |
| | |
| | inlineLoginPromptShown = true; |
| | |
| | const overlay = document.createElement('div'); |
| | overlay.style.position = 'fixed'; |
| | overlay.style.inset = '0'; |
| | overlay.style.backgroundColor = 'rgba(0,0,0,0.35)'; |
| | overlay.style.display = 'flex'; |
| | overlay.style.alignItems = 'center'; |
| | overlay.style.justifyContent = 'center'; |
| | overlay.style.zIndex = '9999'; |
| | |
| | const card = document.createElement('div'); |
| | card.style.width = '100%'; |
| | card.style.maxWidth = '420px'; |
| | card.style.borderRadius = '16px'; |
| | card.style.backgroundColor = 'var(--bs-body-bg)'; |
| | card.style.border = '1px solid var(--bs-border-color)'; |
| | card.style.boxShadow = '0 16px 40px rgba(0,0,0,0.25)'; |
| | card.style.padding = '1.25rem 1.5rem'; |
| | card.style.display = 'flex'; |
| | card.style.flexDirection = 'column'; |
| | card.style.gap = '0.75rem'; |
| | |
| | const title = document.createElement('div'); |
| | title.textContent = "Save your conversations"; |
| | title.style.fontWeight = '600'; |
| | title.style.fontSize = '1rem'; |
| | |
| | const text = document.createElement('div'); |
| | text.textContent = "Log in to keep your chat history across devices. If you continue as a guest, your conversations may be lost later."; |
| | text.style.fontSize = '0.9rem'; |
| | text.style.opacity = '0.9'; |
| | |
| | const buttonRow = document.createElement('div'); |
| | buttonRow.style.display = 'flex'; |
| | buttonRow.style.gap = '0.75rem'; |
| | buttonRow.style.marginTop = '0.5rem'; |
| | |
| | const skipBtn = document.createElement('button'); |
| | skipBtn.textContent = "Continue without login"; |
| | skipBtn.style.flex = '1'; |
| | skipBtn.style.borderRadius = '999px'; |
| | skipBtn.style.border = '1px solid var(--bs-border-color)'; |
| | skipBtn.style.backgroundColor = 'transparent'; |
| | skipBtn.style.color = 'var(--bs-body-color)'; |
| | skipBtn.style.padding = '0.5rem 0.75rem'; |
| | skipBtn.style.fontSize = '0.85rem'; |
| | skipBtn.style.cursor = 'pointer'; |
| | |
| | const loginBtn = document.createElement('button'); |
| | loginBtn.textContent = "Log in to save chats"; |
| | loginBtn.style.flex = '1'; |
| | loginBtn.style.borderRadius = '999px'; |
| | loginBtn.style.border = 'none'; |
| | loginBtn.style.backgroundColor = 'var(--gakr-blue)'; |
| | loginBtn.style.color = '#fff'; |
| | loginBtn.style.padding = '0.5rem 0.75rem'; |
| | loginBtn.style.fontSize = '0.85rem'; |
| | loginBtn.style.cursor = 'pointer'; |
| | |
| | skipBtn.addEventListener('click', function () { |
| | overlay.remove(); |
| | }); |
| | |
| | loginBtn.addEventListener('click', function () { |
| | window.location.href = 'auth.html'; |
| | }); |
| | |
| | buttonRow.appendChild(skipBtn); |
| | buttonRow.appendChild(loginBtn); |
| | card.appendChild(title); |
| | card.appendChild(text); |
| | card.appendChild(buttonRow); |
| | overlay.appendChild(card); |
| | document.body.appendChild(overlay); |
| | } |
| | |
| | |
| | async function sendMessage() { |
| | const text = userInput.value.trim(); |
| | |
| | |
| | if (!text) { |
| | return; |
| | } |
| | |
| | if (!welcomeMessage.classList.contains('d-none')) { |
| | welcomeMessage.classList.add('d-none'); |
| | } |
| | |
| | if (initialMessage.classList.contains('d-none')) { |
| | initialMessage.classList.remove('d-none'); |
| | } |
| | |
| | const filesSnapshot = [...attachedFiles]; |
| | |
| | let userMessageContent = text; |
| | if (filesSnapshot.length > 0) { |
| | const fileNames = filesSnapshot.map(f => f.name).join(', '); |
| | userMessageContent += ` (Attached: ${fileNames})`; |
| | } |
| | addMessage(userMessageContent, 'user'); |
| | messageCount++; |
| | |
| | userInput.value = ''; |
| | attachedFiles = []; |
| | attachedFilePreviewContainer.innerHTML = ''; |
| | autoGrowTextarea(); |
| | userInput.dispatchEvent(new Event('input', { bubbles: true })); |
| | |
| | typingIndicator.classList.remove('d-none'); |
| | scrollToBottom(); |
| | |
| | const formData = new FormData(); |
| | formData.append('api_key', 'gakr-ai-2025-secret'); |
| | formData.append('prompt', text); |
| | |
| | if (filesSnapshot.length > 0) { |
| | filesSnapshot.forEach((file) => { |
| | formData.append('files', file); |
| | }); |
| | } |
| | |
| | try { |
| | const response = await fetch('/api/analyze', { |
| | method: 'POST', |
| | body: formData |
| | }); |
| | |
| | if (!response.ok) { |
| | typingIndicator.classList.add('d-none'); |
| | addMessage('There was a server issue. Please try again later.', 'ai'); |
| | return; |
| | } |
| | |
| | |
| | const aiMessageDiv = addMessage('', 'ai', true); |
| | let accumulatedText = ''; |
| | |
| | const reader = response.body.getReader(); |
| | const decoder = new TextDecoder(); |
| | |
| | while (true) { |
| | const { done, value } = await reader.read(); |
| | if (done) break; |
| | const chunk = decoder.decode(value, { stream: true }); |
| | accumulatedText += chunk; |
| | updateAIMessage(aiMessageDiv, accumulatedText); |
| | } |
| | |
| | if (messageCount > 0 && messageCount % 5 === 0) { |
| | maybeShowInlineLoginPrompt(); |
| | } |
| | } catch (error) { |
| | console.error('Fetch Error:', error); |
| | typingIndicator.classList.add('d-none'); |
| | addMessage('There was a server issue. Please try again later.', 'ai'); |
| | } finally { |
| | if (typingIndicator) { |
| | typingIndicator.classList.add('d-none'); |
| | } |
| | scrollToBottom(); |
| | } |
| | } |
| | |
| | function addMessage(text, type, streaming = false) { |
| | const messageDiv = document.createElement('div'); |
| | messageDiv.className = `gemini-message gemini-message-${type}`; |
| | |
| | const headerDiv = document.createElement('div'); |
| | headerDiv.className = 'gemini-message-header'; |
| | |
| | const avatarDiv = document.createElement('div'); |
| | avatarDiv.className = 'gemini-message-avatar'; |
| | |
| | const avatarIcon = document.createElement('i'); |
| | avatarIcon.className = type === 'user' ? 'fas fa-user' : 'fas fa-robot'; |
| | |
| | const nameSpan = document.createElement('span'); |
| | nameSpan.textContent = type === 'user' ? 'You' : 'GAKR AI'; |
| | |
| | const contentDiv = document.createElement('div'); |
| | contentDiv.className = 'gemini-message-content'; |
| | if (!streaming) { |
| | if (type === 'ai') { |
| | updateAIMessage(contentDiv, text); |
| | } else { |
| | contentDiv.textContent = text; |
| | } |
| | } |
| | |
| | avatarDiv.appendChild(avatarIcon); |
| | headerDiv.appendChild(avatarDiv); |
| | headerDiv.appendChild(nameSpan); |
| | |
| | messageDiv.appendChild(headerDiv); |
| | messageDiv.appendChild(contentDiv); |
| | |
| | chatContainer.appendChild(messageDiv); |
| | |
| | if (!streaming) { |
| | scrollToBottom(); |
| | } |
| | |
| | return streaming ? contentDiv : null; |
| | } |
| | |
| | function updateAIMessage(contentDiv, text) { |
| | |
| | contentDiv.innerHTML = ''; |
| | |
| | const thinkStart = text.indexOf('<think>'); |
| | const thinkEnd = text.indexOf('</think>'); |
| | if (thinkStart !== -1 && thinkEnd !== -1 && thinkEnd > thinkStart) { |
| | const thinking = text.substring(thinkStart + 7, thinkEnd).trim(); |
| | const solution = text.substring(thinkEnd + 8).trim(); |
| | |
| | |
| | const toggleButton = document.createElement('button'); |
| | toggleButton.className = 'gemini-thinking-toggle'; |
| | toggleButton.textContent = 'hide thinking'; |
| | toggleButton.onclick = function() { |
| | const thinkingDiv = this.nextElementSibling; |
| | if (thinkingDiv.style.display === 'none') { |
| | thinkingDiv.style.display = 'block'; |
| | this.textContent = 'hide thinking'; |
| | } else { |
| | thinkingDiv.style.display = 'none'; |
| | this.textContent = 'show thinking'; |
| | } |
| | }; |
| | |
| | |
| | const thinkingDiv = document.createElement('div'); |
| | thinkingDiv.className = 'gemini-thinking-content'; |
| | thinkingDiv.textContent = thinking; |
| | |
| | contentDiv.appendChild(toggleButton); |
| | contentDiv.appendChild(thinkingDiv); |
| | contentDiv.appendChild(document.createTextNode(solution)); |
| | } else { |
| | contentDiv.textContent = text; |
| | } |
| | } |
| | |
| | function scrollToBottom() { |
| | setTimeout(() => { |
| | if (chatContainer) { |
| | chatContainer.scrollTop = chatContainer.scrollHeight; |
| | } |
| | }, 0); |
| | } |
| | |
| | |
| | const urlParams = new URLSearchParams(window.location.search); |
| | const initialQuery = urlParams.get('q'); |
| | |
| | if (initialQuery) { |
| | userInput.value = initialQuery; |
| | userInput.dispatchEvent(new Event('input', { bubbles: true })); |
| | |
| | setTimeout(function() { |
| | if (submitButton && !submitButton.classList.contains('disabled')) { |
| | submitButton.click(); |
| | } |
| | }, 100); |
| | } else { |
| | if (userInput) { |
| | if (userInput.value.trim().length === 0 && attachedFiles.length === 0) { |
| | if (submitButton) submitButton.classList.add('disabled'); |
| | } else { |
| | if (submitButton) submitButton.classList.remove('disabled'); |
| | } |
| | autoGrowTextarea(); |
| | } |
| | } |
| | }); |
| | </script> |
| |
|
| |
|
| |
|
| | </body> |
| | </html> |
| |
|