|
|
<!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); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
.gakr-chat-layout { |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
height: 100vh; |
|
|
width: 100%; |
|
|
background-color: var(--bs-body-bg); |
|
|
color: var(--bs-body-color); |
|
|
} |
|
|
|
|
|
.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; |
|
|
} |
|
|
|
|
|
.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-chat-wrapper { |
|
|
flex: 1; |
|
|
overflow: hidden; |
|
|
position: relative; |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
} |
|
|
|
|
|
.gakr-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; |
|
|
} |
|
|
|
|
|
.gakr-prompt-area { |
|
|
padding: 1rem; |
|
|
border-top: 1px solid var(--bs-border-color); |
|
|
width: 100%; |
|
|
max-width: 800px; |
|
|
margin: 0 auto; |
|
|
|
|
|
position: relative; |
|
|
|
|
|
} |
|
|
|
|
|
.gakr-message { |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
gap: 0.5rem; |
|
|
max-width: 90%; |
|
|
} |
|
|
|
|
|
.gakr-message-user { |
|
|
align-self: flex-end; |
|
|
} |
|
|
|
|
|
.gakr-message-ai { |
|
|
align-self: flex-start; |
|
|
} |
|
|
|
|
|
.gakr-message-header { |
|
|
display: flex; |
|
|
align-items: center; |
|
|
gap: 0.5rem; |
|
|
font-size: 0.85rem; |
|
|
color: var(--bs-secondary-color); |
|
|
} |
|
|
|
|
|
.gakr-message-avatar { |
|
|
width: 24px; |
|
|
height: 24px; |
|
|
border-radius: 50%; |
|
|
display: flex; |
|
|
align-items: center; |
|
|
justify-content: center; |
|
|
font-size: 0.75rem; |
|
|
} |
|
|
|
|
|
.gakr-message-content { |
|
|
padding: 1rem; |
|
|
border-radius: 12px; |
|
|
line-height: 1.5; |
|
|
|
|
|
white-space: pre-wrap; |
|
|
word-break: break-word; |
|
|
} |
|
|
|
|
|
.gakr-message-user .gakr-message-content { |
|
|
background-color: var(--bs-tertiary-bg); |
|
|
border-top-right-radius: 4px; |
|
|
} |
|
|
|
|
|
.gakr-message-ai .gakr-message-content { |
|
|
background-color: rgba(66, 133, 244, 0.1); |
|
|
border-top-left-radius: 4px; |
|
|
} |
|
|
|
|
|
.gakr-message-user .gakr-message-avatar { |
|
|
background-color: var(--bs-tertiary-bg); |
|
|
} |
|
|
|
|
|
.gakr-message-ai .gakr-message-avatar { |
|
|
background-color: rgba(66, 133, 244, 0.2); |
|
|
color: var(--gakr-blue); |
|
|
} |
|
|
|
|
|
|
|
|
.gakr-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; |
|
|
} |
|
|
|
|
|
|
|
|
.gakr-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); |
|
|
} |
|
|
|
|
|
|
|
|
.gakr-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; |
|
|
} |
|
|
|
|
|
.gakr-input:focus { |
|
|
outline: none; |
|
|
} |
|
|
|
|
|
|
|
|
.gakr-input-actions { |
|
|
display: flex; |
|
|
|
|
|
align-items: flex-end; |
|
|
|
|
|
padding-bottom: 0px; |
|
|
|
|
|
gap: 0.5rem; |
|
|
font-size: 1.25rem; |
|
|
flex-shrink: 0; |
|
|
} |
|
|
|
|
|
|
|
|
.gakr-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; |
|
|
} |
|
|
|
|
|
.gakr-action-button:hover { |
|
|
background-color: var(--bs-tertiary-bg); |
|
|
color: var(--bs-body-color); |
|
|
} |
|
|
|
|
|
.gakr-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; |
|
|
} |
|
|
|
|
|
.gakr-submit-button { |
|
|
color: var(--gakr-blue); |
|
|
} |
|
|
|
|
|
|
|
|
.gakr-submit-button:hover { |
|
|
background-color: var(--gakr-blue-light); |
|
|
color: var(--gakr-blue-dark); |
|
|
} |
|
|
|
|
|
.gakr-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; |
|
|
} |
|
|
|
|
|
.gakr-submit-button.disabled { |
|
|
opacity: 0.5; |
|
|
cursor: default; |
|
|
|
|
|
transition: none; |
|
|
} |
|
|
|
|
|
.gakr-submit-button.disabled:hover { |
|
|
background-color: transparent; |
|
|
color: var(--gakr-blue); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.gakr-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; |
|
|
} |
|
|
|
|
|
.gakr-typing-dot { |
|
|
width: 6px; |
|
|
height: 6px; |
|
|
border-radius: 50%; |
|
|
background-color: var(--gakr-blue); |
|
|
animation: typing 1.3s infinite ease-in-out; |
|
|
} |
|
|
|
|
|
.gakr-typing-dot:nth-child(1) { animation-delay: 0s; } |
|
|
.gakr-typing-dot:nth-child(2) { animation-delay: 0.2s; } |
|
|
.gakr-typing-dot:nth-child(3) { animation-delay: 0.4s; } |
|
|
|
|
|
.gakr-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; |
|
|
} |
|
|
|
|
|
.gakr-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; |
|
|
} |
|
|
|
|
|
.gakr-welcome { |
|
|
text-align: center; |
|
|
max-width: 600px; |
|
|
margin: 4rem auto; |
|
|
} |
|
|
|
|
|
.gakr-welcome-icon { |
|
|
font-size: 3rem; |
|
|
margin-bottom: 1.5rem; |
|
|
color: var(--gakr-blue); |
|
|
} |
|
|
|
|
|
.gakr-welcome-title { |
|
|
font-size: 2rem; |
|
|
margin-bottom: 1rem; |
|
|
font-weight: 500; |
|
|
} |
|
|
|
|
|
|
|
|
.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: 1px solid var(--gakr-blue); |
|
|
padding: 0.5rem 1rem; |
|
|
border-radius: 50px; |
|
|
font-size: 0.9rem; |
|
|
transition: background-color 0.2s; |
|
|
display: inline-flex; |
|
|
align-items: center; |
|
|
gap: 0.5rem; |
|
|
} |
|
|
|
|
|
.gakr-profile-button:hover { |
|
|
background-color: rgba(66, 133, 244, 0.1); |
|
|
} |
|
|
|
|
|
|
|
|
.gakr-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; |
|
|
} |
|
|
|
|
|
.gakr-login-prompt.show { |
|
|
opacity: 1; |
|
|
visibility: visible; |
|
|
} |
|
|
|
|
|
.gakr-login-prompt-content { |
|
|
background-color: var(--bs-body-bg); |
|
|
border-radius: 12px; |
|
|
padding: 1.5rem; |
|
|
max-width: 400px; |
|
|
width: 90%; |
|
|
} |
|
|
|
|
|
.gakr-login-prompt-title { |
|
|
font-size: 1.25rem; |
|
|
font-weight: 500; |
|
|
margin-bottom: 1rem; |
|
|
color: var(--gakr-blue); |
|
|
} |
|
|
|
|
|
.gakr-login-prompt-buttons { |
|
|
display: flex; |
|
|
flex-direction: column; |
|
|
gap: 0.75rem; |
|
|
margin-top: 1.5rem; |
|
|
} |
|
|
|
|
|
.gakr-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; |
|
|
} |
|
|
|
|
|
.gakr-button-primary:hover { |
|
|
background-color: #3b78e7; |
|
|
} |
|
|
|
|
|
.gakr-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; |
|
|
} |
|
|
|
|
|
.gakr-button-secondary:hover { |
|
|
background-color: rgba(66, 133, 244, 0.1); |
|
|
} |
|
|
|
|
|
.gakr-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; |
|
|
} |
|
|
|
|
|
.gakr-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="gakr-chat-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"> |
|
|
<a href="/auth" class="gakr-login-button">Sign in / Register</a> |
|
|
</div> |
|
|
</header> |
|
|
|
|
|
<div class="gakr-chat-wrapper"> |
|
|
<div class="gakr-chat-container" id="chatContainer"> |
|
|
<div class="gakr-welcome" id="welcomeMessage"> |
|
|
<div class="gakr-welcome-icon"> |
|
|
<i class="fas fa-robot"></i> |
|
|
</div> |
|
|
<h1 class="gakr-welcome-title">How can I help you today?</h1> |
|
|
<p>I'm GAKR AI, your AI assistant. Ask me anything!</p> |
|
|
</div> |
|
|
|
|
|
<div class="gakr-message gakr-message-ai d-none" id="initialMessage"> |
|
|
<div class="gakr-message-header"> |
|
|
<div class="gakr-message-avatar"> |
|
|
<i class="fas fa-robot"></i> |
|
|
</div> |
|
|
<span>GAKR AI</span> |
|
|
</div> |
|
|
<div class="gakr-message-content">Hello! I'm GAKR AI How can I help you today? |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="gakr-typing d-none" id="typingIndicator"> |
|
|
<div class="gakr-typing-dot"></div> |
|
|
<div class="gakr-typing-dot"></div> |
|
|
<div class="gakr-typing-dot"></div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
<div class="gakr-prompt-area"> |
|
|
<div class="gakr-input-container" id="inputContainer"> |
|
|
<div class="gakr-action-button" id="addButton"> |
|
|
<i class="fas fa-plus"></i> |
|
|
</div> |
|
|
<div class="attached-file-preview-container" id="attachedFilePreviewContainer"> |
|
|
</div> |
|
|
<textarea class="gakr-input" id="userInput" placeholder="Message GAKR AI..." rows="1"></textarea> |
|
|
|
|
|
<div class="gakr-input-actions"> |
|
|
<div class="gakr-action-button"> |
|
|
<i class="fas fa-microphone"></i> |
|
|
</div> |
|
|
<div class="gakr-action-button gakr-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="gakr-login-prompt" id="loginPrompt"> |
|
|
<div class="gakr-login-prompt-content"> |
|
|
<div class="gakr-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="gakr-login-prompt-buttons"> |
|
|
<a href="login.html" class="gakr-button-primary">Sign in / Register</a> |
|
|
<a href="/login?signup=true" class="gakr-button-secondary">Create account</a> |
|
|
<button type="button" class="gakr-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("isLoggedIn") === "true"; |
|
|
|
|
|
|
|
|
updateHeader(); |
|
|
|
|
|
function updateHeader() { |
|
|
const navControls = document.querySelector('.gakr-nav-controls'); |
|
|
if (isLoggedIn) { |
|
|
navControls.innerHTML = '<a href="/profile" class="gakr-profile-button"><i class="fas fa-user"></i> Profile</a>'; |
|
|
} else { |
|
|
navControls.innerHTML = '<a href="/auth" class="gakr-login-button">Sign in / Register</a>'; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
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('.gakr-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(); |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
const microphoneButton = document.querySelector('.gakr-input-actions .gakr-action-button:first-child'); |
|
|
if (microphoneButton) { |
|
|
microphoneButton.addEventListener('click', function() { |
|
|
alert('Voice input feature is currently in development.'); |
|
|
}); |
|
|
} |
|
|
|
|
|
|
|
|
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'; |
|
|
}); |
|
|
|
|
|
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('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 = `gakr-message gakr-message-${type}`; |
|
|
|
|
|
const headerDiv = document.createElement('div'); |
|
|
headerDiv.className = 'gakr-message-header'; |
|
|
|
|
|
const avatarDiv = document.createElement('div'); |
|
|
avatarDiv.className = 'gakr-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 = 'gakr-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 = 'gakr-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 = 'gakr-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> |
|
|
|