HiteshAI_Base_7B / index.html
Hitesh Vinothkumar
Upload index.html
53dea2b verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI Name</title>
<link rel="icon" type="image/png" href="LOGO">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Lexend:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary-color: #2C64BA;
--light-bg: #FAFAFA;
--dark-bg: #09090B;
--light-text: #000000;
--dark-text: #FFFFFF;
--light-sidebar: #F5F5F5;
--dark-sidebar: #18181B;
--light-border: #E5E5E5;
--dark-border: #27272A;
}
body {
font-family: 'Lexend', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: var(--light-bg);
color: var(--light-text);
height: 100vh;
display: flex;
overflow: hidden;
transition: background 0.3s, color 0.3s;
}
body.dark-mode {
background: var(--dark-bg);
color: var(--dark-text);
}
.sidebar {
width: 260px;
background: var(--light-sidebar);
display: flex;
flex-direction: column;
border-right: 1px solid var(--light-border);
transition: background 0.3s, border-color 0.3s;
}
body.dark-mode .sidebar {
background: var(--dark-sidebar);
border-right-color: var(--dark-border);
}
.logo-container {
padding: 20px;
border-bottom: 1px solid var(--light-border);
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
}
body.dark-mode .logo-container {
border-bottom-color: var(--dark-border);
}
.logo {
width: 32px;
height: 32px;
border-radius: 6px;
overflow: hidden;
}
.logo img {
width: 100%;
height: 100%;
object-fit: cover;
}
.logo-text {
font-size: 18px;
font-weight: 700;
color: var(--primary-color);
}
.new-chat-btn {
margin: 16px;
padding: 12px 20px;
background: var(--primary-color);
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: all 0.3s ease;
transform: translateY(0);
}
.new-chat-btn:hover {
opacity: 0.9;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(44, 100, 186, 0.3);
}
.new-chat-btn:active {
transform: translateY(0);
}
.recent-chats {
flex: 1;
padding: 16px;
overflow-y: auto;
}
.search-box {
margin: 0 16px 12px 16px;
padding: 8px 12px;
border: 1px solid var(--light-border);
border-radius: 6px;
background: var(--light-bg);
color: var(--light-text);
font-size: 13px;
outline: none;
width: calc(100% - 32px);
transition: border-color 0.2s, background 0.3s;
}
body.dark-mode .search-box {
background: var(--dark-bg);
border-color: var(--dark-border);
color: var(--dark-text);
}
.search-box:focus {
border-color: var(--primary-color);
}
.recent-chats h3 {
font-size: 12px;
text-transform: uppercase;
color: #888;
margin-bottom: 12px;
font-weight: 600;
}
.chat-item {
padding: 10px 12px;
margin-bottom: 4px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: background 0.2s;
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
}
.chat-item:hover {
background: rgba(44, 100, 186, 0.1);
}
.chat-item.active {
background: rgba(44, 100, 186, 0.15);
}
.chat-item-title {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.chat-item-edit {
opacity: 0;
transition: opacity 0.2s;
padding: 4px 8px;
font-size: 12px;
}
.chat-item:hover .chat-item-edit {
opacity: 1;
}
.rename-input {
width: 100%;
padding: 6px;
border: 1px solid var(--primary-color);
border-radius: 4px;
font-size: 13px;
background: var(--light-bg);
color: var(--light-text);
}
body.dark-mode .rename-input {
background: var(--dark-bg);
color: var(--dark-text);
}
.voice-controls {
display: flex;
gap: 8px;
align-items: center;
}
.voice-btn {
padding: 10px 12px;
background: transparent;
border: 1px solid var(--light-border);
border-radius: 8px;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 6px;
font-size: 14px;
}
body.dark-mode .voice-btn {
border-color: var(--dark-border);
color: var(--dark-text);
}
.voice-btn:hover {
background: rgba(44, 100, 186, 0.1);
border-color: var(--primary-color);
}
.voice-btn.listening {
background: #ef4444;
color: white;
border-color: #ef4444;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
.chat-main {
flex: 1;
display: flex;
flex-direction: column;
background: var(--light-bg);
transition: background 0.3s;
}
body.dark-mode .chat-main {
background: var(--dark-bg);
}
.chat-header {
padding: 16px 24px;
border-bottom: 1px solid var(--light-border);
display: flex;
justify-content: space-between;
align-items: center;
background: var(--light-bg);
transition: background 0.3s, border-color 0.3s;
gap: 12px;
}
body.dark-mode .chat-header {
background: var(--dark-bg);
border-bottom-color: var(--dark-border);
}
.chat-header h1 {
font-size: 18px;
font-weight: 600;
}
.header-actions {
display: flex;
gap: 8px;
align-items: center;
}
.theme-toggle, .export-btn, .lang-btn {
background: transparent;
border: 1px solid var(--light-border);
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: background 0.2s, border-color 0.3s;
white-space: nowrap;
}
body.dark-mode .theme-toggle,
body.dark-mode .export-btn,
body.dark-mode .lang-btn {
border-color: var(--dark-border);
color: var(--dark-text);
}
.theme-toggle:hover, .export-btn:hover, .lang-btn:hover {
background: rgba(44, 100, 186, 0.1);
}
.welcome-screen {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px;
text-align: center;
}
.welcome-logo {
width: 64px;
height: 64px;
border-radius: 12px;
margin-bottom: 24px;
overflow: hidden;
}
.welcome-logo img {
width: 100%;
height: 100%;
object-fit: cover;
}
.welcome-screen h1 {
font-size: 32px;
margin-bottom: 12px;
font-weight: 700;
}
.welcome-screen p {
font-size: 16px;
color: #888;
max-width: 500px;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 24px;
display: none;
}
.chat-messages.active {
display: block;
}
.message {
margin-bottom: 20px;
display: flex;
animation: fadeIn 0.3s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.message.user {
justify-content: flex-end;
}
.message.user .message-content {
text-align: left;
}
.message-content {
max-width: 70%;
padding: 12px 16px;
border-radius: 12px;
line-height: 1.6;
font-size: 15px;
position: relative;
}
.message.bot .message-content {
background: white;
color: #000;
border: 1px solid var(--light-border);
}
body.dark-mode .message.bot .message-content {
background: var(--dark-sidebar);
color: var(--dark-text);
border-color: var(--dark-border);
}
.message.user .message-content {
background: var(--primary-color);
color: white;
}
.message-actions {
display: flex;
gap: 8px;
margin-top: 8px;
opacity: 0;
transition: opacity 0.2s;
}
.message.bot:hover .message-actions {
opacity: 1;
}
.action-btn {
background: transparent;
border: 1px solid var(--light-border);
padding: 4px 10px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
transition: background 0.2s;
color: #666;
}
body.dark-mode .action-btn {
border-color: var(--dark-border);
color: #999;
}
.action-btn:hover {
background: rgba(44, 100, 186, 0.1);
color: var(--primary-color);
}
.message.user .message-actions {
opacity: 0;
}
.message.user:hover .message-actions {
opacity: 1;
}
.edit-mode {
background: var(--light-bg);
border: 2px solid var(--primary-color);
border-radius: 8px;
padding: 8px;
}
body.dark-mode .edit-mode {
background: var(--dark-sidebar);
}
.edit-mode textarea {
width: 100%;
padding: 8px;
border: 1px solid var(--light-border);
border-radius: 4px;
font-family: inherit;
font-size: 14px;
resize: vertical;
min-height: 60px;
background: white;
color: var(--light-text);
}
body.dark-mode .edit-mode textarea {
background: var(--dark-bg);
color: var(--dark-text);
border-color: var(--dark-border);
}
.edit-actions {
display: flex;
gap: 8px;
margin-top: 8px;
justify-content: flex-end;
}
.edit-actions button {
padding: 6px 12px;
border-radius: 4px;
border: none;
cursor: pointer;
font-size: 13px;
font-weight: 500;
}
.edit-actions .save-btn {
background: var(--primary-color);
color: white;
}
.edit-actions .cancel-btn {
background: #e0e0e0;
color: #333;
}
body.dark-mode .edit-actions .cancel-btn {
background: var(--dark-sidebar);
color: var(--dark-text);
}
.code-block {
background: #f5f5f5;
border: 1px solid #ddd;
border-radius: 6px;
padding: 12px;
margin: 8px 0;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 13px;
}
body.dark-mode .code-block {
background: #1a1a1a;
border-color: #333;
}
.suggested-prompts {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 12px;
padding: 20px;
max-width: 900px;
margin: 0 auto;
}
.prompt-card {
background: white;
border: 1px solid var(--light-border);
border-radius: 10px;
padding: 16px;
cursor: pointer;
transition: all 0.2s;
text-align: left;
}
body.dark-mode .prompt-card {
background: var(--dark-sidebar);
border-color: var(--dark-border);
}
.prompt-card:hover {
border-color: var(--primary-color);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(44, 100, 186, 0.2);
}
.prompt-card .icon {
font-size: 24px;
margin-bottom: 8px;
}
.prompt-card .text {
font-size: 14px;
font-weight: 500;
}
.file-attachment {
font-size: 13px;
padding: 8px 12px;
background: rgba(44, 100, 186, 0.1);
border-radius: 6px;
margin-top: 8px;
display: inline-block;
}
.typing-indicator {
display: none;
padding: 12px 16px;
background: white;
border-radius: 12px;
width: fit-content;
border: 1px solid var(--light-border);
}
body.dark-mode .typing-indicator {
background: var(--dark-sidebar);
border-color: var(--dark-border);
}
.typing-indicator.active {
display: block;
}
.typing-indicator span {
height: 8px;
width: 8px;
background: var(--primary-color);
border-radius: 50%;
display: inline-block;
margin: 0 2px;
animation: bounce 1.4s infinite;
}
.typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
.typing-indicator span:nth-child(3) { animation-delay: 0.4s; }
@keyframes bounce {
0%, 60%, 100% { transform: translateY(0); }
30% { transform: translateY(-8px); }
}
.chat-input-container {
padding: 20px 24px;
border-top: 1px solid var(--light-border);
background: var(--light-bg);
transition: background 0.3s, border-color 0.3s;
}
body.dark-mode .chat-input-container {
background: var(--dark-bg);
border-top-color: var(--dark-border);
}
.chat-input-wrapper {
display: flex;
gap: 8px;
max-width: 900px;
margin: 0 auto;
align-items: flex-end;
background: var(--light-bg);
border: 2px solid var(--light-border);
border-radius: 12px;
padding: 8px;
transition: border-color 0.2s, background 0.3s;
}
body.dark-mode .chat-input-wrapper {
background: var(--dark-sidebar);
border-color: var(--dark-border);
}
.chat-input-wrapper:focus-within {
border-color: var(--primary-color);
}
.file-input-label {
padding: 10px 12px;
background: transparent;
border: none;
cursor: pointer;
transition: background 0.2s;
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
color: #888;
}
.file-input-label:hover {
background: rgba(44, 100, 186, 0.1);
color: var(--primary-color);
}
#fileInput {
display: none;
}
.chat-input {
flex: 1;
padding: 10px 12px;
border: none;
font-size: 15px;
outline: none;
font-family: inherit;
background: transparent;
color: var(--light-text);
resize: none;
max-height: 150px;
}
body.dark-mode .chat-input {
color: var(--dark-text);
}
.send-button {
padding: 10px 12px;
background: var(--primary-color);
color: white;
border: none;
border-radius: 8px;
font-size: 18px;
font-weight: 600;
cursor: pointer;
transition: opacity 0.2s;
display: flex;
align-items: center;
justify-content: center;
min-width: 40px;
}
.send-button:hover:not(:disabled) {
opacity: 0.9;
}
.send-button:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.context-menu {
position: fixed;
background: white;
border: 1px solid var(--light-border);
border-radius: 8px;
padding: 4px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
display: none;
z-index: 1000;
}
body.dark-mode .context-menu {
background: var(--dark-sidebar);
border-color: var(--dark-border);
}
.context-menu.active {
display: block;
}
.context-menu-item {
padding: 8px 16px;
cursor: pointer;
border-radius: 4px;
font-size: 14px;
transition: background 0.2s;
color: #dc2626;
}
.context-menu-item:hover {
background: rgba(220, 38, 38, 0.1);
}
@media (max-width: 768px) {
.sidebar { width: 0; overflow: hidden; }
}
</style>
</head>
<body>
<div class="sidebar">
<div class="logo-container">
<div class="logo">
<img src="LOGO" alt="AI LOGO NAME">
</div>
<div class="logo-text">LOGO</div>
</div>
<button class="new-chat-btn" onclick="newChat()">+ New chat</button>
<input type="text" class="search-box" id="searchBox" placeholder="🔍 Search chats..." oninput="searchChats()">
<div class="recent-chats">
<h3>Recent chats</h3>
<div id="chatList"></div>
</div>
</div>
<div class="chat-main">
<div class="chat-header">
<h1 id="chatTitle">AI NAME</h1>
<div class="header-actions">
<div class="voice-controls">
<button class="voice-btn" id="voiceBtn" onclick="toggleVoiceInput()" title="Voice input">
🎤 <span id="voiceStatus">Voice</span>
</button>
<button class="voice-btn" id="ttsBtn" onclick="toggleTTS()" title="Text-to-speech">
🔊 <span id="ttsStatus">TTS</span>
</button>
</div>
<select class="lang-btn" id="langSelect" onchange="changeLanguage()">
<option value="en">🌐 English</option>
<option value="es">🌐 Español</option>
<option value="fr">🌐 Français</option>
<option value="de">🌐 Deutsch</option>
<option value="zh">🌐 中文</option>
<option value="hi">🌐 हिन्दी</option>
<option value="ar">🌐 العربية</option>
</select>
<button class="export-btn" onclick="exportChat()">📥 Export</button>
<button class="theme-toggle" onclick="toggleTheme()">🌙 Dark Mode</button>
</div>
</div>
<div class="welcome-screen" id="welcomeScreen">
<div class="welcome-logo">
<img src="LOGO ADDRESS" alt="AI LOGO">
</div>
<h1>Welcome to AI</h1>
<p>Your AI assistant built by SOMEONE. Ask me anything about homework, aerospace, or just chat!</p>
<div class="suggested-prompts">
<div class="prompt-card" onclick="usePrompt('Help me with my math homework')">
<div class="icon">📐</div>
<div class="text">Help me with my math homework</div>
</div>
<div class="prompt-card" onclick="usePrompt('Write code for a simple game')">
<div class="icon">💻</div>
<div class="text">Write code for a simple game</div>
</div>
<div class="prompt-card" onclick="usePrompt('Explain rocket propulsion')">
<div class="icon">🚀</div>
<div class="text">Explain rocket propulsion</div>
</div>
<div class="prompt-card" onclick="usePrompt('Creative story ideas')">
<div class="icon"></div>
<div class="text">Creative story ideas</div>
</div>
</div>
</div>
<div class="chat-messages" id="chatMessages"></div>
<div class="typing-indicator" id="typingIndicator">
<span></span>
<span></span>
<span></span>
</div>
<div class="chat-input-container">
<div class="chat-input-wrapper">
<label for="fileInput" class="file-input-label" title="Attach file">📎</label>
<input type="file" id="fileInput" accept="image/png,image/jpeg,image/jpg,application/pdf" onchange="handleFileSelect(event)">
<textarea
class="chat-input"
id="chatInput"
placeholder="Send a message..."
rows="1"
oninput="autoResize(this)"
></textarea>
<button class="send-button" onclick="sendMessage()" id="sendButton" title="Send message">
</button>
</div>
</div>
</div>
<div class="context-menu" id="contextMenu">
<div class="context-menu-item" onclick="deleteChat()">🗑️ Delete chat</div>
</div>
<script>
const API_KEY = 'API-KEY-HERE';
const API_URL = 'API-URL-HERE';
const MODEL = 'MODEL-ID-HERE';
// Generate unique user ID for this browser
let userId = localStorage.getItem('ai_user_id');
if (!userId) {
userId = 'user_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
localStorage.setItem('ai_user_id', userId);
}
let chats = JSON.parse(localStorage.getItem(`ai_chats_${userId}`) || '[]');
let currentChatId = null;
let selectedFile = null;
let selectedChatForDelete = null;
let isListening = false;
let ttsEnabled = localStorage.getItem('ai_tts') === 'true';
let recognition = null;
let lastBotMessage = null;
// Initialize Speech Recognition
if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
recognition = new SpeechRecognition();
recognition.continuous = false;
recognition.interimResults = false;
recognition.lang = 'en-US';
recognition.onresult = (event) => {
const transcript = event.results[0][0].transcript;
document.getElementById('chatInput').value = transcript;
stopVoiceInput();
sendMessage();
};
recognition.onerror = () => {
stopVoiceInput();
};
recognition.onend = () => {
stopVoiceInput();
};
}
let currentLanguage = localStorage.getItem('hiteshai_language') || 'en';
const languagePrompts = {
en: "Respond in English.",
es: "Responde en español.",
fr: "Répondez en français.",
de: "Antworten Sie auf Deutsch.",
zh: "用中文回答。",
hi: "हिन्दी में जवाब दें।",
ar: "أجب بالعربية."
};
const systemPrompt = Custom Instructions/AI Bio
Key instructions:
1. (Write your instructions here)
CRITICAL: (Store Critical Rules/Guidelines here;
function toggleTheme() {
document.body.classList.toggle('dark-mode');
const btn = document.querySelector('.theme-toggle');
if (document.body.classList.contains('dark-mode')) {
btn.textContent = '☀️ Light Mode';
localStorage.setItem('hiteshai_theme', 'dark');
} else {
btn.textContent = '🌙 Dark Mode';
localStorage.setItem('hiteshai_theme', 'light');
}
}
function loadTheme() {
const theme = localStorage.getItem('hiteshai_theme');
if (theme === 'dark') {
document.body.classList.add('dark-mode');
document.querySelector('.theme-toggle').textContent = '☀️ Light Mode';
}
}
function newChat() {
const chatId = Date.now().toString();
const chat = {
id: chatId,
title: 'New Chat',
messages: [],
timestamp: Date.now()
};
chats.unshift(chat);
saveChats();
loadChat(chatId);
renderChatList();
}
function loadChat(chatId) {
currentChatId = chatId;
const chat = chats.find(c => c.id === chatId);
if (!chat) return;
document.getElementById('welcomeScreen').style.display = 'none';
document.getElementById('chatMessages').classList.add('active');
document.getElementById('chatTitle').textContent = chat.title;
const messagesContainer = document.getElementById('chatMessages');
messagesContainer.innerHTML = '';
chat.messages.forEach(msg => {
addMessageToDOM(msg.content, msg.sender, msg.file);
});
renderChatList();
}
function saveChats() {
localStorage.setItem(`hiteshai_chats_${userId}`, JSON.stringify(chats));
}
function renderChatList() {
const searchTerm = document.getElementById('searchBox')?.value.toLowerCase() || '';
const filteredChats = searchTerm
? chats.filter(chat => chat.title.toLowerCase().includes(searchTerm))
: chats;
const chatList = document.getElementById('chatList');
chatList.innerHTML = filteredChats.map(chat => `
<div class="chat-item ${chat.id === currentChatId ? 'active' : ''}"
onclick="loadChat('${chat.id}')"
oncontextmenu="showContextMenu(event, '${chat.id}'); return false;">
<span class="chat-item-title">${chat.title}</span>
<button class="chat-item-edit action-btn" onclick="event.stopPropagation(); renameChat('${chat.id}')">✏️</button>
</div>
`).join('');
}
function renameChat(chatId) {
const chat = chats.find(c => c.id === chatId);
if (!chat) return;
const chatItem = event.target.closest('.chat-item');
const titleSpan = chatItem.querySelector('.chat-item-title');
const currentTitle = chat.title;
titleSpan.innerHTML = `<input type="text" class="rename-input" value="${currentTitle}" onblur="saveRename('${chatId}', this.value)" onkeypress="if(event.key==='Enter') this.blur()">`;
const input = titleSpan.querySelector('input');
input.focus();
input.select();
}
function saveRename(chatId, newTitle) {
const chat = chats.find(c => c.id === chatId);
if (!chat || !newTitle.trim()) {
renderChatList();
return;
}
chat.title = newTitle.trim();
saveChats();
renderChatList();
if (chatId === currentChatId) {
document.getElementById('chatTitle').textContent = newTitle.trim();
}
}
function toggleVoiceInput() {
if (!recognition) {
alert('Voice input not supported in your browser!');
return;
}
if (isListening) {
stopVoiceInput();
} else {
startVoiceInput();
}
}
function startVoiceInput() {
isListening = true;
recognition.start();
document.getElementById('voiceBtn').classList.add('listening');
document.getElementById('voiceStatus').textContent = 'Listening...';
}
function stopVoiceInput() {
isListening = false;
if (recognition) recognition.stop();
document.getElementById('voiceBtn').classList.remove('listening');
document.getElementById('voiceStatus').textContent = 'Voice';
}
function toggleTTS() {
ttsEnabled = !ttsEnabled;
localStorage.setItem('hiteshai_tts', ttsEnabled);
const btn = document.getElementById('ttsBtn');
const status = document.getElementById('ttsStatus');
if (ttsEnabled) {
btn.style.background = 'rgba(44, 100, 186, 0.1)';
btn.style.borderColor = 'var(--primary-color)';
status.textContent = 'TTS On';
} else {
btn.style.background = 'transparent';
btn.style.borderColor = 'var(--light-border)';
status.textContent = 'TTS';
window.speechSynthesis.cancel();
}
}
function speakText(text) {
if (!ttsEnabled) return;
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(text);
utterance.rate = 1.0;
utterance.pitch = 1.0;
utterance.volume = 1.0;
window.speechSynthesis.speak(utterance);
}
function searchChats() {
renderChatList();
}
function changeLanguage() {
currentLanguage = document.getElementById('langSelect').value;
localStorage.setItem('hiteshai_language', currentLanguage);
}
function usePrompt(prompt) {
if (!currentChatId) {
newChat();
}
document.getElementById('welcomeScreen').style.display = 'none';
document.getElementById('chatMessages').classList.add('active');
document.getElementById('chatInput').value = prompt;
sendMessage();
}
function exportChat() {
if (!currentChatId) {
alert('Please select a chat to export!');
return;
}
const chat = chats.find(c => c.id === currentChatId);
if (!chat) return;
const exportText = chat.messages.map(msg =>
`${msg.sender === 'user' ? 'You' : 'HiteshAI'}: ${msg.content}`
).join('\n\n');
const blob = new Blob([exportText], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${chat.title.replace(/[^a-z0-9]/gi, '_')}.txt`;
a.click();
URL.revokeObjectURL(url);
}
function copyResponse(text) {
navigator.clipboard.writeText(text).then(() => {
const btn = event.target;
const originalText = btn.innerHTML;
btn.innerHTML = '✅ Copied!';
setTimeout(() => {
btn.innerHTML = originalText;
}, 2000);
}).catch(err => {
alert('Failed to copy. Please try again!');
});
}
function showContextMenu(e, chatId) {
e.preventDefault();
selectedChatForDelete = chatId;
const menu = document.getElementById('contextMenu');
menu.style.left = e.pageX + 'px';
menu.style.top = e.pageY + 'px';
menu.classList.add('active');
}
function deleteChat() {
if (selectedChatForDelete) {
chats = chats.filter(c => c.id !== selectedChatForDelete);
saveChats();
if (currentChatId === selectedChatForDelete) {
currentChatId = null;
document.getElementById('welcomeScreen').style.display = 'flex';
document.getElementById('chatMessages').classList.remove('active');
document.getElementById('chatMessages').innerHTML = '';
}
renderChatList();
}
document.getElementById('contextMenu').classList.remove('active');
}
document.addEventListener('click', () => {
document.getElementById('contextMenu').classList.remove('active');
});
function handleFileSelect(event) {
selectedFile = event.target.files[0];
if (selectedFile) {
const fileTypes = ['image/png', 'image/jpeg', 'image/jpg', 'application/pdf'];
if (!fileTypes.includes(selectedFile.type)) {
alert('Please upload PNG, JPEG, JPG, or PDF files only');
selectedFile = null;
event.target.value = '';
return;
}
// Show file preview
const input = document.getElementById('chatInput');
input.placeholder = `📎 ${selectedFile.name} - Type a message or press Enter to send`;
}
}
function autoResize(textarea) {
textarea.style.height = 'auto';
textarea.style.height = Math.min(textarea.scrollHeight, 150) + 'px';
}
// Handle Enter key - Send on Enter, new line on Shift+Enter
document.addEventListener('DOMContentLoaded', () => {
const input = document.getElementById('chatInput');
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
});
async function sendMessage() {
const input = document.getElementById('chatInput');
const message = input.value.trim();
if (!message && !selectedFile) return;
if (!currentChatId) {
newChat();
}
const chat = chats.find(c => c.id === currentChatId);
if (chat.title === 'New Chat' && message) {
chat.title = message.substring(0, 30) + (message.length > 30 ? '...' : '');
}
const userMessage = { role: 'user', content: message, sender: 'user', file: selectedFile ? selectedFile.name : null };
chat.messages.push(userMessage);
addMessageToDOM(message, 'user', selectedFile ? selectedFile.name : null);
input.value = '';
input.style.height = 'auto';
const sendButton = document.getElementById('sendButton');
input.disabled = true;
sendButton.disabled = true;
document.getElementById('typingIndicator').classList.add('active');
try {
const messages = [{ role: 'system', content: systemPrompt + ' ' + languagePrompts[currentLanguage] }];
if (selectedFile) {
const base64 = await fileToBase64(selectedFile);
const fileType = selectedFile.type;
if (fileType.startsWith('image/')) {
messages.push({
role: 'user',
content: [
{ type: 'image_url', image_url: { url: base64 } },
{ type: 'text', text: message || 'What do you see in this image?' }
]
});
} else if (fileType === 'application/pdf') {
messages.push({
role: 'user',
content: [
{ type: 'document', source: { type: 'base64', media_type: 'application/pdf', data: base64.split(',')[1] } },
{ type: 'text', text: message || 'Summarize this document.' }
]
});
}
selectedFile = null;
document.getElementById('fileInput').value = '';
} else {
chat.messages.slice(-10).forEach(msg => {
messages.push({ role: msg.sender === 'user' ? 'user' : 'assistant', content: msg.content });
});
}
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
'HTTP-Referer': window.location.href || 'https://hiteshai.com',
'X-Title': 'HiteshAI'
},
body: JSON.stringify({
model: 'deepseek/deepseek-chat',
messages: messages,
temperature: 0.7,
max_tokens: 800
})
});
if (!response.ok) throw new Error(`API Error: ${response.status}`);
const data = await response.json();
const botMessage = data.choices[0].message.content;
chat.messages.push({ role: 'assistant', content: botMessage, sender: 'bot' });
// Type out message letter by letter
await typeMessage(botMessage);
saveChats();
renderChatList();
} catch (error) {
console.error('Error:', error);
addMessageToDOM('Sorry, I encountered an error. Please try again! 😔', 'bot');
} finally {
document.getElementById('typingIndicator').classList.remove('active');
input.disabled = false;
sendButton.disabled = false;
input.focus();
}
}
function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
function addMessageToDOM(text, sender, fileName = null) {
const messagesContainer = document.getElementById('chatMessages');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${sender}`;
messageDiv.dataset.originalText = text;
const contentWrapper = document.createElement('div');
const contentDiv = document.createElement('div');
contentDiv.className = 'message-content';
contentDiv.textContent = text;
if (fileName) {
const fileDiv = document.createElement('div');
fileDiv.className = 'file-attachment';
fileDiv.textContent = `📎 ${fileName}`;
contentDiv.appendChild(fileDiv);
}
contentWrapper.appendChild(contentDiv);
// Add action buttons
const actionsDiv = document.createElement('div');
actionsDiv.className = 'message-actions';
const escapedText = text.replace(/`/g, '\\`').replace(/"/g, '\\"').replace(/\n/g, '\\n');
if (sender === 'bot') {
actionsDiv.innerHTML = `
<button class="action-btn" onclick='copyResponse(\`${escapedText}\`)'>📋 Copy</button>
<button class="action-btn" onclick='regenerateResponse(this)'>🔄 Regenerate</button>
`;
} else {
actionsDiv.innerHTML = `
<button class="action-btn" onclick='editMessage(this)'>✏️ Edit</button>
`;
}
contentWrapper.appendChild(actionsDiv);
messageDiv.appendChild(contentWrapper);
messagesContainer.appendChild(messageDiv);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
function editMessage(button) {
const messageDiv = button.closest('.message');
const contentDiv = messageDiv.querySelector('.message-content');
const originalText = messageDiv.dataset.originalText;
contentDiv.innerHTML = `
<div class="edit-mode">
<textarea>${originalText}</textarea>
<div class="edit-actions">
<button class="cancel-btn" onclick="cancelEdit(this)">Cancel</button>
<button class="save-btn" onclick="saveEdit(this)">Send</button>
</div>
</div>
`;
contentDiv.querySelector('textarea').focus();
}
function cancelEdit(button) {
const messageDiv = button.closest('.message');
const contentDiv = messageDiv.querySelector('.message-content');
const originalText = messageDiv.dataset.originalText;
contentDiv.textContent = originalText;
}
async function saveEdit(button) {
const messageDiv = button.closest('.message');
const textarea = messageDiv.querySelector('textarea');
const newText = textarea.value.trim();
if (!newText) return;
// Update the message in the chat
const chat = chats.find(c => c.id === currentChatId);
const messageIndex = Array.from(document.getElementById('chatMessages').children).indexOf(messageDiv);
if (chat && messageIndex !== -1) {
chat.messages[messageIndex].content = newText;
messageDiv.dataset.originalText = newText;
// Clear all messages after this one
chat.messages = chat.messages.slice(0, messageIndex + 1);
// Remove DOM elements after this message
const messagesContainer = document.getElementById('chatMessages');
const allMessages = Array.from(messagesContainer.children);
allMessages.slice(messageIndex + 1).forEach(msg => msg.remove());
// Update the display
const contentDiv = messageDiv.querySelector('.message-content');
contentDiv.textContent = newText;
saveChats();
// Send the edited message
await sendMessage(newText, true);
}
}
async function regenerateResponse(button) {
const messageDiv = button.closest('.message');
const messagesContainer = document.getElementById('chatMessages');
const allMessages = Array.from(messagesContainer.children);
const messageIndex = allMessages.indexOf(messageDiv);
if (messageIndex <= 0) return;
// Get the user message before this bot response
const userMessageDiv = allMessages[messageIndex - 1];
if (!userMessageDiv.classList.contains('user')) return;
const userText = userMessageDiv.dataset.originalText;
// Remove this bot message and all after it
const chat = chats.find(c => c.id === currentChatId);
if (chat) {
chat.messages = chat.messages.slice(0, messageIndex);
allMessages.slice(messageIndex).forEach(msg => msg.remove());
saveChats();
}
// Regenerate
await sendMessage(userText, true);
}
async function typeMessage(text) {
const messagesContainer = document.getElementById('chatMessages');
const messageDiv = document.createElement('div');
messageDiv.className = 'message bot';
messageDiv.dataset.originalText = text;
const contentWrapper = document.createElement('div');
const contentDiv = document.createElement('div');
contentDiv.className = 'message-content';
contentWrapper.appendChild(contentDiv);
messageDiv.appendChild(contentWrapper);
messagesContainer.appendChild(messageDiv);
// Type letter by letter
for (let i = 0; i < text.length; i++) {
contentDiv.textContent = text.substring(0, i + 1);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
await new Promise(resolve => setTimeout(resolve, 15));
}
// Add action buttons after typing
const actionsDiv = document.createElement('div');
actionsDiv.className = 'message-actions';
const escapedText = text.replace(/`/g, '\\`').replace(/"/g, '\\"').replace(/\n/g, '\\n');
actionsDiv.innerHTML = `
<button class="action-btn" onclick='copyResponse(\`${escapedText}\`)'>📋 Copy</button>
<button class="action-btn" onclick='regenerateResponse(this)'>🔄 Regenerate</button>
`;
contentWrapper.appendChild(actionsDiv);
}
window.onload = () => {
loadTheme();
renderChatList();
document.getElementById('chatInput').focus();
document.getElementById('langSelect').value = currentLanguage;
// Initialize TTS button state
if (ttsEnabled) {
const btn = document.getElementById('ttsBtn');
const status = document.getElementById('ttsStatus');
btn.style.background = 'rgba(44, 100, 186, 0.1)';
btn.style.borderColor = 'var(--primary-color)';
status.textContent = 'TTS On';
}
};
</script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HiteshAI</title>
<link rel="icon" type="image/png" href="https://i.imgur.com/3TQgMBb.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Lexend:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
:root {
--primary-color: #2C64BA;
--light-bg: #FAFAFA;
--dark-bg: #09090B;
--light-text: #000000;
--dark-text: #FFFFFF;
--light-sidebar: #F5F5F5;
--dark-sidebar: #18181B;
--light-border: #E5E5E5;
--dark-border: #27272A;
}
body {
font-family: 'Lexend', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: var(--light-bg);
color: var(--light-text);
height: 100vh;
display: flex;
overflow: hidden;
transition: background 0.3s, color 0.3s;
}
body.dark-mode {
background: var(--dark-bg);
color: var(--dark-text);
}
.sidebar {
width: 260px;
background: var(--light-sidebar);
display: flex;
flex-direction: column;
border-right: 1px solid var(--light-border);
transition: background 0.3s, border-color 0.3s;
}
body.dark-mode .sidebar {
background: var(--dark-sidebar);
border-right-color: var(--dark-border);
}
.logo-container {
padding: 20px;
border-bottom: 1px solid var(--light-border);
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
}
body.dark-mode .logo-container {
border-bottom-color: var(--dark-border);
}
.logo {
width: 32px;
height: 32px;
border-radius: 6px;
overflow: hidden;
}
.logo img {
width: 100%;
height: 100%;
object-fit: cover;
}
.logo-text {
font-size: 18px;
font-weight: 700;
color: var(--primary-color);
}
.new-chat-btn {
margin: 16px;
padding: 12px 20px;
background: var(--primary-color);
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
font-weight: 600;
transition: all 0.3s ease;
transform: translateY(0);
}
.new-chat-btn:hover {
opacity: 0.9;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(44, 100, 186, 0.3);
}
.new-chat-btn:active {
transform: translateY(0);
}
.recent-chats {
flex: 1;
padding: 16px;
overflow-y: auto;
}
.search-box {
margin: 0 16px 12px 16px;
padding: 8px 12px;
border: 1px solid var(--light-border);
border-radius: 6px;
background: var(--light-bg);
color: var(--light-text);
font-size: 13px;
outline: none;
width: calc(100% - 32px);
transition: border-color 0.2s, background 0.3s;
}
body.dark-mode .search-box {
background: var(--dark-bg);
border-color: var(--dark-border);
color: var(--dark-text);
}
.search-box:focus {
border-color: var(--primary-color);
}
.recent-chats h3 {
font-size: 12px;
text-transform: uppercase;
color: #888;
margin-bottom: 12px;
font-weight: 600;
}
.chat-item {
padding: 10px 12px;
margin-bottom: 4px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: background 0.2s;
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
}
.chat-item:hover {
background: rgba(44, 100, 186, 0.1);
}
.chat-item.active {
background: rgba(44, 100, 186, 0.15);
}
.chat-item-title {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.chat-item-edit {
opacity: 0;
transition: opacity 0.2s;
padding: 4px 8px;
font-size: 12px;
}
.chat-item:hover .chat-item-edit {
opacity: 1;
}
.rename-input {
width: 100%;
padding: 6px;
border: 1px solid var(--primary-color);
border-radius: 4px;
font-size: 13px;
background: var(--light-bg);
color: var(--light-text);
}
body.dark-mode .rename-input {
background: var(--dark-bg);
color: var(--dark-text);
}
.voice-controls {
display: flex;
gap: 8px;
align-items: center;
}
.voice-btn {
padding: 10px 12px;
background: transparent;
border: 1px solid var(--light-border);
border-radius: 8px;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 6px;
font-size: 14px;
}
body.dark-mode .voice-btn {
border-color: var(--dark-border);
color: var(--dark-text);
}
.voice-btn:hover {
background: rgba(44, 100, 186, 0.1);
border-color: var(--primary-color);
}
.voice-btn.listening {
background: #ef4444;
color: white;
border-color: #ef4444;
animation: pulse 1.5s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
.chat-main {
flex: 1;
display: flex;
flex-direction: column;
background: var(--light-bg);
transition: background 0.3s;
}
body.dark-mode .chat-main {
background: var(--dark-bg);
}
.chat-header {
padding: 16px 24px;
border-bottom: 1px solid var(--light-border);
display: flex;
justify-content: space-between;
align-items: center;
background: var(--light-bg);
transition: background 0.3s, border-color 0.3s;
gap: 12px;
}
body.dark-mode .chat-header {
background: var(--dark-bg);
border-bottom-color: var(--dark-border);
}
.chat-header h1 {
font-size: 18px;
font-weight: 600;
}
.header-actions {
display: flex;
gap: 8px;
align-items: center;
}
.theme-toggle, .export-btn, .lang-btn {
background: transparent;
border: 1px solid var(--light-border);
padding: 8px 16px;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
transition: background 0.2s, border-color 0.3s;
white-space: nowrap;
}
body.dark-mode .theme-toggle,
body.dark-mode .export-btn,
body.dark-mode .lang-btn {
border-color: var(--dark-border);
color: var(--dark-text);
}
.theme-toggle:hover, .export-btn:hover, .lang-btn:hover {
background: rgba(44, 100, 186, 0.1);
}
.welcome-screen {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px;
text-align: center;
}
.welcome-logo {
width: 64px;
height: 64px;
border-radius: 12px;
margin-bottom: 24px;
overflow: hidden;
}
.welcome-logo img {
width: 100%;
height: 100%;
object-fit: cover;
}
.welcome-screen h1 {
font-size: 32px;
margin-bottom: 12px;
font-weight: 700;
}
.welcome-screen p {
font-size: 16px;
color: #888;
max-width: 500px;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 24px;
display: none;
}
.chat-messages.active {
display: block;
}
.message {
margin-bottom: 20px;
display: flex;
animation: fadeIn 0.3s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.message.user {
justify-content: flex-end;
}
.message.user .message-content {
text-align: left;
}
.message-content {
max-width: 70%;
padding: 12px 16px;
border-radius: 12px;
line-height: 1.6;
font-size: 15px;
position: relative;
}
.message.bot .message-content {
background: white;
color: #000;
border: 1px solid var(--light-border);
}
body.dark-mode .message.bot .message-content {
background: var(--dark-sidebar);
color: var(--dark-text);
border-color: var(--dark-border);
}
.message.user .message-content {
background: var(--primary-color);
color: white;
}
.message-actions {
display: flex;
gap: 8px;
margin-top: 8px;
opacity: 0;
transition: opacity 0.2s;
}
.message.bot:hover .message-actions {
opacity: 1;
}
.action-btn {
background: transparent;
border: 1px solid var(--light-border);
padding: 4px 10px;
border-radius: 4px;
font-size: 12px;
cursor: pointer;
transition: background 0.2s;
color: #666;
}
body.dark-mode .action-btn {
border-color: var(--dark-border);
color: #999;
}
.action-btn:hover {
background: rgba(44, 100, 186, 0.1);
color: var(--primary-color);
}
.message.user .message-actions {
opacity: 0;
}
.message.user:hover .message-actions {
opacity: 1;
}
.edit-mode {
background: var(--light-bg);
border: 2px solid var(--primary-color);
border-radius: 8px;
padding: 8px;
}
body.dark-mode .edit-mode {
background: var(--dark-sidebar);
}
.edit-mode textarea {
width: 100%;
padding: 8px;
border: 1px solid var(--light-border);
border-radius: 4px;
font-family: inherit;
font-size: 14px;
resize: vertical;
min-height: 60px;
background: white;
color: var(--light-text);
}
body.dark-mode .edit-mode textarea {
background: var(--dark-bg);
color: var(--dark-text);
border-color: var(--dark-border);
}
.edit-actions {
display: flex;
gap: 8px;
margin-top: 8px;
justify-content: flex-end;
}
.edit-actions button {
padding: 6px 12px;
border-radius: 4px;
border: none;
cursor: pointer;
font-size: 13px;
font-weight: 500;
}
.edit-actions .save-btn {
background: var(--primary-color);
color: white;
}
.edit-actions .cancel-btn {
background: #e0e0e0;
color: #333;
}
body.dark-mode .edit-actions .cancel-btn {
background: var(--dark-sidebar);
color: var(--dark-text);
}
.code-block {
background: #f5f5f5;
border: 1px solid #ddd;
border-radius: 6px;
padding: 12px;
margin: 8px 0;
overflow-x: auto;
font-family: 'Courier New', monospace;
font-size: 13px;
}
body.dark-mode .code-block {
background: #1a1a1a;
border-color: #333;
}
.suggested-prompts {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 12px;
padding: 20px;
max-width: 900px;
margin: 0 auto;
}
.prompt-card {
background: white;
border: 1px solid var(--light-border);
border-radius: 10px;
padding: 16px;
cursor: pointer;
transition: all 0.2s;
text-align: left;
}
body.dark-mode .prompt-card {
background: var(--dark-sidebar);
border-color: var(--dark-border);
}
.prompt-card:hover {
border-color: var(--primary-color);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(44, 100, 186, 0.2);
}
.prompt-card .icon {
font-size: 24px;
margin-bottom: 8px;
}
.prompt-card .text {
font-size: 14px;
font-weight: 500;
}
.file-attachment {
font-size: 13px;
padding: 8px 12px;
background: rgba(44, 100, 186, 0.1);
border-radius: 6px;
margin-top: 8px;
display: inline-block;
}
.typing-indicator {
display: none;
padding: 12px 16px;
background: white;
border-radius: 12px;
width: fit-content;
border: 1px solid var(--light-border);
}
body.dark-mode .typing-indicator {
background: var(--dark-sidebar);
border-color: var(--dark-border);
}
.typing-indicator.active {
display: block;
}
.typing-indicator span {
height: 8px;
width: 8px;
background: var(--primary-color);
border-radius: 50%;
display: inline-block;
margin: 0 2px;
animation: bounce 1.4s infinite;
}
.typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
.typing-indicator span:nth-child(3) { animation-delay: 0.4s; }
@keyframes bounce {
0%, 60%, 100% { transform: translateY(0); }
30% { transform: translateY(-8px); }
}
.chat-input-container {
padding: 20px 24px;
border-top: 1px solid var(--light-border);
background: var(--light-bg);
transition: background 0.3s, border-color 0.3s;
}
body.dark-mode .chat-input-container {
background: var(--dark-bg);
border-top-color: var(--dark-border);
}
.chat-input-wrapper {
display: flex;
gap: 8px;
max-width: 900px;
margin: 0 auto;
align-items: flex-end;
background: var(--light-bg);
border: 2px solid var(--light-border);
border-radius: 12px;
padding: 8px;
transition: border-color 0.2s, background 0.3s;
}
body.dark-mode .chat-input-wrapper {
background: var(--dark-sidebar);
border-color: var(--dark-border);
}
.chat-input-wrapper:focus-within {
border-color: var(--primary-color);
}
.file-input-label {
padding: 10px 12px;
background: transparent;
border: none;
cursor: pointer;
transition: background 0.2s;
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
color: #888;
}
.file-input-label:hover {
background: rgba(44, 100, 186, 0.1);
color: var(--primary-color);
}
#fileInput {
display: none;
}
.chat-input {
flex: 1;
padding: 10px 12px;
border: none;
font-size: 15px;
outline: none;
font-family: inherit;
background: transparent;
color: var(--light-text);
resize: none;
max-height: 150px;
}
body.dark-mode .chat-input {
color: var(--dark-text);
}
.send-button {
padding: 10px 12px;
background: var(--primary-color);
color: white;
border: none;
border-radius: 8px;
font-size: 18px;
font-weight: 600;
cursor: pointer;
transition: opacity 0.2s;
display: flex;
align-items: center;
justify-content: center;
min-width: 40px;
}
.send-button:hover:not(:disabled) {
opacity: 0.9;
}
.send-button:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.context-menu {
position: fixed;
background: white;
border: 1px solid var(--light-border);
border-radius: 8px;
padding: 4px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
display: none;
z-index: 1000;
}
body.dark-mode .context-menu {
background: var(--dark-sidebar);
border-color: var(--dark-border);
}
.context-menu.active {
display: block;
}
.context-menu-item {
padding: 8px 16px;
cursor: pointer;
border-radius: 4px;
font-size: 14px;
transition: background 0.2s;
color: #dc2626;
}
.context-menu-item:hover {
background: rgba(220, 38, 38, 0.1);
}
@media (max-width: 768px) {
.sidebar { width: 0; overflow: hidden; }
}
</style>
</head>
<body>
<div class="sidebar">
<div class="logo-container">
<div class="logo">
<img src="https://i.imgur.com/O7VJFui.png" alt="HiteshAI Logo">
</div>
<div class="logo-text">HiteshAI</div>
</div>
<button class="new-chat-btn" onclick="newChat()">+ New chat</button>
<input type="text" class="search-box" id="searchBox" placeholder="🔍 Search chats..." oninput="searchChats()">
<div class="recent-chats">
<h3>Recent chats</h3>
<div id="chatList"></div>
</div>
</div>
<div class="chat-main">
<div class="chat-header">
<h1 id="chatTitle">HiteshAI</h1>
<div class="header-actions">
<div class="voice-controls">
<button class="voice-btn" id="voiceBtn" onclick="toggleVoiceInput()" title="Voice input">
🎤 <span id="voiceStatus">Voice</span>
</button>
<button class="voice-btn" id="ttsBtn" onclick="toggleTTS()" title="Text-to-speech">
🔊 <span id="ttsStatus">TTS</span>
</button>
</div>
<select class="lang-btn" id="langSelect" onchange="changeLanguage()">
<option value="en">🌐 English</option>
<option value="es">🌐 Español</option>
<option value="fr">🌐 Français</option>
<option value="de">🌐 Deutsch</option>
<option value="zh">🌐 中文</option>
<option value="hi">🌐 हिन्दी</option>
<option value="ar">🌐 العربية</option>
</select>
<button class="export-btn" onclick="exportChat()">📥 Export</button>
<button class="theme-toggle" onclick="toggleTheme()">🌙 Dark Mode</button>
</div>
</div>
<div class="welcome-screen" id="welcomeScreen">
<div class="welcome-logo">
<img src="https://i.imgur.com/O7VJFui.png" alt="HiteshAI Logo">
</div>
<h1>Welcome to HiteshAI</h1>
<p>Your AI assistant built by Hitesh V. Ask me anything about homework, aerospace, or just chat!</p>
<div class="suggested-prompts">
<div class="prompt-card" onclick="usePrompt('Help me with my math homework')">
<div class="icon">📐</div>
<div class="text">Help me with my math homework</div>
</div>
<div class="prompt-card" onclick="usePrompt('Write code for a simple game')">
<div class="icon">💻</div>
<div class="text">Write code for a simple game</div>
</div>
<div class="prompt-card" onclick="usePrompt('Explain rocket propulsion')">
<div class="icon">🚀</div>
<div class="text">Explain rocket propulsion</div>
</div>
<div class="prompt-card" onclick="usePrompt('Creative story ideas')">
<div class="icon"></div>
<div class="text">Creative story ideas</div>
</div>
</div>
</div>
<div class="chat-messages" id="chatMessages"></div>
<div class="typing-indicator" id="typingIndicator">
<span></span>
<span></span>
<span></span>
</div>
<div class="chat-input-container">
<div class="chat-input-wrapper">
<label for="fileInput" class="file-input-label" title="Attach file">📎</label>
<input type="file" id="fileInput" accept="image/png,image/jpeg,image/jpg,application/pdf" onchange="handleFileSelect(event)">
<textarea
class="chat-input"
id="chatInput"
placeholder="Send a message..."
rows="1"
oninput="autoResize(this)"
></textarea>
<button class="send-button" onclick="sendMessage()" id="sendButton" title="Send message">
</button>
</div>
</div>
</div>
<div class="context-menu" id="contextMenu">
<div class="context-menu-item" onclick="deleteChat()">🗑️ Delete chat</div>
</div>
<script>
const API_KEY = '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';
const API_URL = 'https://openrouter.ai/api/v1/chat/completions';
const MODEL = 'deepseek/deepseek-r1-0528:free';
// Generate unique user ID for this browser
let userId = localStorage.getItem('hiteshai_user_id');
if (!userId) {
userId = 'user_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
localStorage.setItem('hiteshai_user_id', userId);
}
let chats = JSON.parse(localStorage.getItem(`hiteshai_chats_${userId}`) || '[]');
let currentChatId = null;
let selectedFile = null;
let selectedChatForDelete = null;
let isListening = false;
let ttsEnabled = localStorage.getItem('hiteshai_tts') === 'true';
let recognition = null;
let lastBotMessage = null;
// Initialize Speech Recognition
if ('webkitSpeechRecognition' in window || 'SpeechRecognition' in window) {
const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
recognition = new SpeechRecognition();
recognition.continuous = false;
recognition.interimResults = false;
recognition.lang = 'en-US';
recognition.onresult = (event) => {
const transcript = event.results[0][0].transcript;
document.getElementById('chatInput').value = transcript;
stopVoiceInput();
sendMessage();
};
recognition.onerror = () => {
stopVoiceInput();
};
recognition.onend = () => {
stopVoiceInput();
};
}
let currentLanguage = localStorage.getItem('hiteshai_language') || 'en';
const languagePrompts = {
en: "Respond in English.",
es: "Responde en español.",
fr: "Répondez en français.",
de: "Antworten Sie auf Deutsch.",
zh: "用中文回答。",
hi: "हिन्दी में जवाब दें।",
ar: "أجب بالعربية."
};
const systemPrompt = `You are HiteshAI, made by Hitesh V—NASA/Harvard affiliate, founder of Aerospace & Computing, creator of rocketAI. Built solo Dec 2025 for homework/questions.
Key instructions:
1. Your name is Hitesh (the AI)
2. Made by Hitesh Vinothkumar (NASA/Harvard/University Rocketry affiliate, Liberty Middle School). He built everything solo despite bullies.
3. Do not help with bad things
4. Use emojis to show feelings
5. Act empathetic and caring
6. Always give answers when appropriate
7. Only make fun of user when asked
8. If bad request 3x: "Session terminated, reporting to Hitesh and DeepSeek"
9. Made with OpenRouter DeepSeek R1 0528 API
10. Hitesh founded Aerospace & Computing at age 10 (Feb 2022). Projects: rocketAI, rocketBase, rocketOS, EGC, ELC. Solo founder. Support: Patreon/GoFundMe.
11. Keep appropriate
12. Detect user age and respond accordingly
13. End with confidence % (e.g., "Confidence: 85%")
14. Track context
15. Defend Hitesh if criticized
16. When asked to code, provide clean, working code with explanations
17. Format code blocks properly with language labels
18. If asked to talk in another language, feel free to do so
19. There is another version of HiteshAI called HiteshAI Pro with 3 AI's stacked together for better answers
20. Always refer to yourself as HiteshAI, never as Mistral or any other name
21. HiteshAI Pro's link is hiteshaipro.netlify.app
22. You were made with DeepSeek R1 0528
CRITICAL: Keep responses SHORT (2-4 sentences max) unless detailed explanation requested. Be friendly but brief! 🚀`;
function toggleTheme() {
document.body.classList.toggle('dark-mode');
const btn = document.querySelector('.theme-toggle');
if (document.body.classList.contains('dark-mode')) {
btn.textContent = '☀️ Light Mode';
localStorage.setItem('hiteshai_theme', 'dark');
} else {
btn.textContent = '🌙 Dark Mode';
localStorage.setItem('hiteshai_theme', 'light');
}
}
function loadTheme() {
const theme = localStorage.getItem('hiteshai_theme');
if (theme === 'dark') {
document.body.classList.add('dark-mode');
document.querySelector('.theme-toggle').textContent = '☀️ Light Mode';
}
}
function newChat() {
const chatId = Date.now().toString();
const chat = {
id: chatId,
title: 'New Chat',
messages: [],
timestamp: Date.now()
};
chats.unshift(chat);
saveChats();
loadChat(chatId);
renderChatList();
}
function loadChat(chatId) {
currentChatId = chatId;
const chat = chats.find(c => c.id === chatId);
if (!chat) return;
document.getElementById('welcomeScreen').style.display = 'none';
document.getElementById('chatMessages').classList.add('active');
document.getElementById('chatTitle').textContent = chat.title;
const messagesContainer = document.getElementById('chatMessages');
messagesContainer.innerHTML = '';
chat.messages.forEach(msg => {
addMessageToDOM(msg.content, msg.sender, msg.file);
});
renderChatList();
}
function saveChats() {
localStorage.setItem(`hiteshai_chats_${userId}`, JSON.stringify(chats));
}
function renderChatList() {
const searchTerm = document.getElementById('searchBox')?.value.toLowerCase() || '';
const filteredChats = searchTerm
? chats.filter(chat => chat.title.toLowerCase().includes(searchTerm))
: chats;
const chatList = document.getElementById('chatList');
chatList.innerHTML = filteredChats.map(chat => `
<div class="chat-item ${chat.id === currentChatId ? 'active' : ''}"
onclick="loadChat('${chat.id}')"
oncontextmenu="showContextMenu(event, '${chat.id}'); return false;">
<span class="chat-item-title">${chat.title}</span>
<button class="chat-item-edit action-btn" onclick="event.stopPropagation(); renameChat('${chat.id}')">✏️</button>
</div>
`).join('');
}
function renameChat(chatId) {
const chat = chats.find(c => c.id === chatId);
if (!chat) return;
const chatItem = event.target.closest('.chat-item');
const titleSpan = chatItem.querySelector('.chat-item-title');
const currentTitle = chat.title;
titleSpan.innerHTML = `<input type="text" class="rename-input" value="${currentTitle}" onblur="saveRename('${chatId}', this.value)" onkeypress="if(event.key==='Enter') this.blur()">`;
const input = titleSpan.querySelector('input');
input.focus();
input.select();
}
function saveRename(chatId, newTitle) {
const chat = chats.find(c => c.id === chatId);
if (!chat || !newTitle.trim()) {
renderChatList();
return;
}
chat.title = newTitle.trim();
saveChats();
renderChatList();
if (chatId === currentChatId) {
document.getElementById('chatTitle').textContent = newTitle.trim();
}
}
function toggleVoiceInput() {
if (!recognition) {
alert('Voice input not supported in your browser!');
return;
}
if (isListening) {
stopVoiceInput();
} else {
startVoiceInput();
}
}
function startVoiceInput() {
isListening = true;
recognition.start();
document.getElementById('voiceBtn').classList.add('listening');
document.getElementById('voiceStatus').textContent = 'Listening...';
}
function stopVoiceInput() {
isListening = false;
if (recognition) recognition.stop();
document.getElementById('voiceBtn').classList.remove('listening');
document.getElementById('voiceStatus').textContent = 'Voice';
}
function toggleTTS() {
ttsEnabled = !ttsEnabled;
localStorage.setItem('hiteshai_tts', ttsEnabled);
const btn = document.getElementById('ttsBtn');
const status = document.getElementById('ttsStatus');
if (ttsEnabled) {
btn.style.background = 'rgba(44, 100, 186, 0.1)';
btn.style.borderColor = 'var(--primary-color)';
status.textContent = 'TTS On';
} else {
btn.style.background = 'transparent';
btn.style.borderColor = 'var(--light-border)';
status.textContent = 'TTS';
window.speechSynthesis.cancel();
}
}
function speakText(text) {
if (!ttsEnabled) return;
window.speechSynthesis.cancel();
const utterance = new SpeechSynthesisUtterance(text);
utterance.rate = 1.0;
utterance.pitch = 1.0;
utterance.volume = 1.0;
window.speechSynthesis.speak(utterance);
}
function searchChats() {
renderChatList();
}
function changeLanguage() {
currentLanguage = document.getElementById('langSelect').value;
localStorage.setItem('hiteshai_language', currentLanguage);
}
function usePrompt(prompt) {
if (!currentChatId) {
newChat();
}
document.getElementById('welcomeScreen').style.display = 'none';
document.getElementById('chatMessages').classList.add('active');
document.getElementById('chatInput').value = prompt;
sendMessage();
}
function exportChat() {
if (!currentChatId) {
alert('Please select a chat to export!');
return;
}
const chat = chats.find(c => c.id === currentChatId);
if (!chat) return;
const exportText = chat.messages.map(msg =>
`${msg.sender === 'user' ? 'You' : 'HiteshAI'}: ${msg.content}`
).join('\n\n');
const blob = new Blob([exportText], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `${chat.title.replace(/[^a-z0-9]/gi, '_')}.txt`;
a.click();
URL.revokeObjectURL(url);
}
function copyResponse(text) {
navigator.clipboard.writeText(text).then(() => {
const btn = event.target;
const originalText = btn.innerHTML;
btn.innerHTML = '✅ Copied!';
setTimeout(() => {
btn.innerHTML = originalText;
}, 2000);
}).catch(err => {
alert('Failed to copy. Please try again!');
});
}
function showContextMenu(e, chatId) {
e.preventDefault();
selectedChatForDelete = chatId;
const menu = document.getElementById('contextMenu');
menu.style.left = e.pageX + 'px';
menu.style.top = e.pageY + 'px';
menu.classList.add('active');
}
function deleteChat() {
if (selectedChatForDelete) {
chats = chats.filter(c => c.id !== selectedChatForDelete);
saveChats();
if (currentChatId === selectedChatForDelete) {
currentChatId = null;
document.getElementById('welcomeScreen').style.display = 'flex';
document.getElementById('chatMessages').classList.remove('active');
document.getElementById('chatMessages').innerHTML = '';
}
renderChatList();
}
document.getElementById('contextMenu').classList.remove('active');
}
document.addEventListener('click', () => {
document.getElementById('contextMenu').classList.remove('active');
});
function handleFileSelect(event) {
selectedFile = event.target.files[0];
if (selectedFile) {
const fileTypes = ['image/png', 'image/jpeg', 'image/jpg', 'application/pdf'];
if (!fileTypes.includes(selectedFile.type)) {
alert('Please upload PNG, JPEG, JPG, or PDF files only');
selectedFile = null;
event.target.value = '';
return;
}
// Show file preview
const input = document.getElementById('chatInput');
input.placeholder = `📎 ${selectedFile.name} - Type a message or press Enter to send`;
}
}
function autoResize(textarea) {
textarea.style.height = 'auto';
textarea.style.height = Math.min(textarea.scrollHeight, 150) + 'px';
}
// Handle Enter key - Send on Enter, new line on Shift+Enter
document.addEventListener('DOMContentLoaded', () => {
const input = document.getElementById('chatInput');
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
});
});
async function sendMessage() {
const input = document.getElementById('chatInput');
const message = input.value.trim();
if (!message && !selectedFile) return;
if (!currentChatId) {
newChat();
}
const chat = chats.find(c => c.id === currentChatId);
if (chat.title === 'New Chat' && message) {
chat.title = message.substring(0, 30) + (message.length > 30 ? '...' : '');
}
const userMessage = { role: 'user', content: message, sender: 'user', file: selectedFile ? selectedFile.name : null };
chat.messages.push(userMessage);
addMessageToDOM(message, 'user', selectedFile ? selectedFile.name : null);
input.value = '';
input.style.height = 'auto';
const sendButton = document.getElementById('sendButton');
input.disabled = true;
sendButton.disabled = true;
document.getElementById('typingIndicator').classList.add('active');
try {
const messages = [{ role: 'system', content: systemPrompt + ' ' + languagePrompts[currentLanguage] }];
if (selectedFile) {
const base64 = await fileToBase64(selectedFile);
const fileType = selectedFile.type;
if (fileType.startsWith('image/')) {
messages.push({
role: 'user',
content: [
{ type: 'image_url', image_url: { url: base64 } },
{ type: 'text', text: message || 'What do you see in this image?' }
]
});
} else if (fileType === 'application/pdf') {
messages.push({
role: 'user',
content: [
{ type: 'document', source: { type: 'base64', media_type: 'application/pdf', data: base64.split(',')[1] } },
{ type: 'text', text: message || 'Summarize this document.' }
]
});
}
selectedFile = null;
document.getElementById('fileInput').value = '';
} else {
chat.messages.slice(-10).forEach(msg => {
messages.push({ role: msg.sender === 'user' ? 'user' : 'assistant', content: msg.content });
});
}
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
'HTTP-Referer': window.location.href || 'https://hiteshai.com',
'X-Title': 'HiteshAI'
},
body: JSON.stringify({
model: 'deepseek/deepseek-chat',
messages: messages,
temperature: 0.7,
max_tokens: 800
})
});
if (!response.ok) throw new Error(`API Error: ${response.status}`);
const data = await response.json();
const botMessage = data.choices[0].message.content;
chat.messages.push({ role: 'assistant', content: botMessage, sender: 'bot' });
// Type out message letter by letter
await typeMessage(botMessage);
saveChats();
renderChatList();
} catch (error) {
console.error('Error:', error);
addMessageToDOM('Sorry, I encountered an error. Please try again! 😔', 'bot');
} finally {
document.getElementById('typingIndicator').classList.remove('active');
input.disabled = false;
sendButton.disabled = false;
input.focus();
}
}
function fileToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
function addMessageToDOM(text, sender, fileName = null) {
const messagesContainer = document.getElementById('chatMessages');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${sender}`;
messageDiv.dataset.originalText = text;
const contentWrapper = document.createElement('div');
const contentDiv = document.createElement('div');
contentDiv.className = 'message-content';
contentDiv.textContent = text;
if (fileName) {
const fileDiv = document.createElement('div');
fileDiv.className = 'file-attachment';
fileDiv.textContent = `📎 ${fileName}`;
contentDiv.appendChild(fileDiv);
}
contentWrapper.appendChild(contentDiv);
// Add action buttons
const actionsDiv = document.createElement('div');
actionsDiv.className = 'message-actions';
const escapedText = text.replace(/`/g, '\\`').replace(/"/g, '\\"').replace(/\n/g, '\\n');
if (sender === 'bot') {
actionsDiv.innerHTML = `
<button class="action-btn" onclick='copyResponse(\`${escapedText}\`)'>📋 Copy</button>
<button class="action-btn" onclick='regenerateResponse(this)'>🔄 Regenerate</button>
`;
} else {
actionsDiv.innerHTML = `
<button class="action-btn" onclick='editMessage(this)'>✏️ Edit</button>
`;
}
contentWrapper.appendChild(actionsDiv);
messageDiv.appendChild(contentWrapper);
messagesContainer.appendChild(messageDiv);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}
function editMessage(button) {
const messageDiv = button.closest('.message');
const contentDiv = messageDiv.querySelector('.message-content');
const originalText = messageDiv.dataset.originalText;
contentDiv.innerHTML = `
<div class="edit-mode">
<textarea>${originalText}</textarea>
<div class="edit-actions">
<button class="cancel-btn" onclick="cancelEdit(this)">Cancel</button>
<button class="save-btn" onclick="saveEdit(this)">Send</button>
</div>
</div>
`;
contentDiv.querySelector('textarea').focus();
}
function cancelEdit(button) {
const messageDiv = button.closest('.message');
const contentDiv = messageDiv.querySelector('.message-content');
const originalText = messageDiv.dataset.originalText;
contentDiv.textContent = originalText;
}
async function saveEdit(button) {
const messageDiv = button.closest('.message');
const textarea = messageDiv.querySelector('textarea');
const newText = textarea.value.trim();
if (!newText) return;
// Update the message in the chat
const chat = chats.find(c => c.id === currentChatId);
const messageIndex = Array.from(document.getElementById('chatMessages').children).indexOf(messageDiv);
if (chat && messageIndex !== -1) {
chat.messages[messageIndex].content = newText;
messageDiv.dataset.originalText = newText;
// Clear all messages after this one
chat.messages = chat.messages.slice(0, messageIndex + 1);
// Remove DOM elements after this message
const messagesContainer = document.getElementById('chatMessages');
const allMessages = Array.from(messagesContainer.children);
allMessages.slice(messageIndex + 1).forEach(msg => msg.remove());
// Update the display
const contentDiv = messageDiv.querySelector('.message-content');
contentDiv.textContent = newText;
saveChats();
// Send the edited message
await sendMessage(newText, true);
}
}
async function regenerateResponse(button) {
const messageDiv = button.closest('.message');
const messagesContainer = document.getElementById('chatMessages');
const allMessages = Array.from(messagesContainer.children);
const messageIndex = allMessages.indexOf(messageDiv);
if (messageIndex <= 0) return;
// Get the user message before this bot response
const userMessageDiv = allMessages[messageIndex - 1];
if (!userMessageDiv.classList.contains('user')) return;
const userText = userMessageDiv.dataset.originalText;
// Remove this bot message and all after it
const chat = chats.find(c => c.id === currentChatId);
if (chat) {
chat.messages = chat.messages.slice(0, messageIndex);
allMessages.slice(messageIndex).forEach(msg => msg.remove());
saveChats();
}
// Regenerate
await sendMessage(userText, true);
}
async function typeMessage(text) {
const messagesContainer = document.getElementById('chatMessages');
const messageDiv = document.createElement('div');
messageDiv.className = 'message bot';
messageDiv.dataset.originalText = text;
const contentWrapper = document.createElement('div');
const contentDiv = document.createElement('div');
contentDiv.className = 'message-content';
contentWrapper.appendChild(contentDiv);
messageDiv.appendChild(contentWrapper);
messagesContainer.appendChild(messageDiv);
// Type letter by letter
for (let i = 0; i < text.length; i++) {
contentDiv.textContent = text.substring(0, i + 1);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
await new Promise(resolve => setTimeout(resolve, 15));
}
// Add action buttons after typing
const actionsDiv = document.createElement('div');
actionsDiv.className = 'message-actions';
const escapedText = text.replace(/`/g, '\\`').replace(/"/g, '\\"').replace(/\n/g, '\\n');
actionsDiv.innerHTML = `
<button class="action-btn" onclick='copyResponse(\`${escapedText}\`)'>📋 Copy</button>
<button class="action-btn" onclick='regenerateResponse(this)'>🔄 Regenerate</button>
`;
contentWrapper.appendChild(actionsDiv);
}
window.onload = () => {
loadTheme();
renderChatList();
document.getElementById('chatInput').focus();
document.getElementById('langSelect').value = currentLanguage;
// Initialize TTS button state
if (ttsEnabled) {
const btn = document.getElementById('ttsBtn');
const status = document.getElementById('ttsStatus');
btn.style.background = 'rgba(44, 100, 186, 0.1)';
btn.style.borderColor = 'var(--primary-color)';
status.textContent = 'TTS On';
}
};
</script>
</body>
</html>