| <!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';
|
|
|
|
|
| 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;
|
|
|
|
|
|
|
| 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;
|
| }
|
|
|
|
|
| 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';
|
| }
|
|
|
|
|
|
|
| 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' });
|
|
|
|
|
| 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);
|
|
|
|
|
| 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;
|
|
|
|
|
|
|
| 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;
|
|
|
|
|
| chat.messages = chat.messages.slice(0, messageIndex + 1);
|
|
|
|
|
| const messagesContainer = document.getElementById('chatMessages');
|
| const allMessages = Array.from(messagesContainer.children);
|
| allMessages.slice(messageIndex + 1).forEach(msg => msg.remove());
|
|
|
|
|
| const contentDiv = messageDiv.querySelector('.message-content');
|
| contentDiv.textContent = newText;
|
|
|
| saveChats();
|
|
|
|
|
| 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;
|
|
|
|
|
| const userMessageDiv = allMessages[messageIndex - 1];
|
| if (!userMessageDiv.classList.contains('user')) return;
|
|
|
| const userText = userMessageDiv.dataset.originalText;
|
|
|
|
|
| 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();
|
| }
|
|
|
|
|
| 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);
|
|
|
|
|
| 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));
|
| }
|
|
|
|
|
| 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;
|
|
|
|
|
| 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';
|
|
|
|
|
| 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;
|
|
|
|
|
|
|
| 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;
|
| }
|
|
|
|
|
| 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';
|
| }
|
|
|
|
|
|
|
| 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' });
|
|
|
|
|
| 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);
|
|
|
|
|
| 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;
|
|
|
|
|
|
|
| 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;
|
|
|
|
|
| chat.messages = chat.messages.slice(0, messageIndex + 1);
|
|
|
|
|
| const messagesContainer = document.getElementById('chatMessages');
|
| const allMessages = Array.from(messagesContainer.children);
|
| allMessages.slice(messageIndex + 1).forEach(msg => msg.remove());
|
|
|
|
|
| const contentDiv = messageDiv.querySelector('.message-content');
|
| contentDiv.textContent = newText;
|
|
|
| saveChats();
|
|
|
|
|
| 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;
|
|
|
|
|
| const userMessageDiv = allMessages[messageIndex - 1];
|
| if (!userMessageDiv.classList.contains('user')) return;
|
|
|
| const userText = userMessageDiv.dataset.originalText;
|
|
|
|
|
| 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();
|
| }
|
|
|
|
|
| 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);
|
|
|
|
|
| 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));
|
| }
|
|
|
|
|
| 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;
|
|
|
|
|
| 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> |