Download Index.html from nruto/naruto: direct link, hf CLI and curl.
- Browser
- Download file 69.2 kB
-
https://huggingface.co/spaces/nruto/naruto/resolve/main/Index.html
- Command line
-
hf download hf://spaces/nruto/naruto/Index.html
-
curl -L -o Index.html https://huggingface.co/spaces/nruto/naruto/resolve/main/Index.html
69.2 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>AI Chat Assistant</title> | |
| <style> | |
| :root { | |
| --primary-color: #2dd4bf; | |
| --secondary-color: #1e293b; | |
| --bg-color: #0f172a; | |
| --text-primary: #f8fafc; | |
| --text-secondary: #94a3b8; | |
| --accent-color: #818cf8; | |
| --border-color: #334155; | |
| --hover-color: #1e293b; | |
| --gradient-1: linear-gradient(135deg, #2dd4bf20, #818cf820); | |
| --gradient-2: linear-gradient(45deg, #2dd4bf, #818cf8); | |
| --glow-shadow: 0 0 20px rgba(45, 212, 191, 0.2); | |
| --message-user-bg: linear-gradient(135deg, #2dd4bf, #818cf8); | |
| --message-ai-bg: rgba(30, 41, 59, 0.5); | |
| --message-user-gradient: linear-gradient(135deg, #2563eb, #1d4ed8); | |
| --message-user-text: #ffffff; | |
| --message-user-shadow: 0 4px 15px rgba(45, 212, 191, 0.2); | |
| } | |
| body { | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; | |
| margin: 0; | |
| padding: 0; | |
| background-color: var(--bg-color); | |
| color: var(--text-primary); | |
| display: grid; | |
| grid-template-columns: 260px 1fr; | |
| min-height: 100vh; | |
| background: var(--bg-color); | |
| background-image: | |
| radial-gradient(circle at 10% 20%, rgba(0, 255, 170, 0.05) 0%, transparent 20%), | |
| radial-gradient(circle at 90% 80%, rgba(0, 200, 255, 0.05) 0%, transparent 20%); | |
| transition: all 0.3s ease; | |
| } | |
| /* Sidebar Styles */ | |
| .sidebar { | |
| background-color: var(--secondary-color); | |
| backdrop-filter: blur(10px); | |
| border-right: 1px solid rgba(255, 255, 255, 0.1); | |
| padding: 16px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 16px; | |
| animation: slideIn 0.5s ease; | |
| } | |
| .new-chat-btn { | |
| background: var(--message-user-bg); | |
| color: white; | |
| border: none; | |
| padding: 12px; | |
| border-radius: 12px; | |
| cursor: pointer; | |
| font-weight: 600; | |
| transition: all 0.3s ease; | |
| box-shadow: var(--message-user-shadow); | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .new-chat-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 6px 20px rgba(45, 212, 191, 0.3); | |
| background: var(--accent-color); | |
| } | |
| .chat-list { | |
| flex-grow: 1; | |
| overflow-y: auto; | |
| } | |
| .chat-item { | |
| padding: 10px; | |
| border-radius: 6px; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| color: var(--text-secondary); | |
| transition: all 0.3s ease; | |
| background: transparent; | |
| border: 1px solid transparent; | |
| } | |
| .chat-item:hover { | |
| background: var(--hover-color); | |
| border-color: var(--primary-color); | |
| transform: translateX(5px); | |
| } | |
| .chat-item.active { | |
| background: var(--hover-color); | |
| border-left: 2px solid var(--primary-color); | |
| } | |
| .delete-chat { | |
| opacity: 0; | |
| transition: opacity 0.2s ease; | |
| color: var(--text-secondary); | |
| background: none; | |
| border: none; | |
| cursor: pointer; | |
| padding: 4px; | |
| } | |
| .chat-item:hover .delete-chat { | |
| opacity: 1; | |
| } | |
| /* Main Chat Area */ | |
| .main-container { | |
| display: flex; | |
| flex-direction: column; | |
| height: 100vh; | |
| } | |
| .chat-header { | |
| padding: 16px; | |
| border-bottom: 1px solid var(--border-color); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| position: relative; | |
| background: rgba(26, 31, 44, 0.8); | |
| backdrop-filter: blur(10px); | |
| border-bottom: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .chat-title { | |
| font-size: 1.1rem; | |
| color: var(--primary-color); | |
| margin: 0; | |
| text-align: center; | |
| font-weight: 500; | |
| max-width: 60%; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| white-space: nowrap; | |
| text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3); | |
| letter-spacing: 0.5px; | |
| } | |
| .chat-messages { | |
| flex-grow: 1; | |
| overflow-y: auto; | |
| padding: 20px; | |
| scroll-behavior: smooth; | |
| height: calc(100vh - 140px); | |
| font-size: 16px ; | |
| } | |
| .message { | |
| max-width: 80%; | |
| padding: 12px 16px; | |
| margin-bottom: 16px; | |
| font-size: 16px ; | |
| line-height: 1.5; | |
| border-radius: 18px; | |
| position: relative; | |
| display: inline-flex; | |
| gap: 12px; | |
| align-items: flex-start; | |
| width: fit-content; | |
| } | |
| .message.user-message { | |
| margin-left: auto; | |
| background: linear-gradient(135deg, #2dd4bf, #0ea5e9); | |
| border-bottom-right-radius: 4px; | |
| flex-direction: row-reverse; | |
| box-shadow: 0 2px 8px rgba(45, 212, 191, 0.2); | |
| float: right; | |
| clear: both; | |
| } | |
| .message.ai-message { | |
| margin-right: auto; | |
| background: var(--secondary-color); | |
| border-bottom-left-radius: 4px; | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | |
| float: left; | |
| clear: both; | |
| } | |
| .avatar { | |
| width: 32px; | |
| height: 32px; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-weight: 600; | |
| font-size: 14px; | |
| flex-shrink: 0; | |
| margin-top: 4px; | |
| } | |
| .user-avatar { | |
| background: white; | |
| color: var(--primary-color); | |
| box-shadow: 0 2px 8px rgba(45, 212, 191, 0.2); | |
| } | |
| .ai-avatar { | |
| background: white; | |
| color: var(--secondary-color); | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); | |
| } | |
| .message-content { | |
| font-size: 16px ; | |
| line-height: 1.5; | |
| padding: 0 4px; | |
| word-wrap: break-word; | |
| white-space: pre-wrap; | |
| } | |
| .user-message .message-content { | |
| color: white; | |
| text-align: left; | |
| } | |
| .ai-message .message-content { | |
| color: var(--text-primary); | |
| } | |
| /* Input Area */ | |
| .input-container { | |
| padding: 20px; | |
| border-top: 1px solid var(--border-color); | |
| max-width: 900px; | |
| margin: 0 auto; | |
| width: calc(100% - 40px); | |
| position: relative; | |
| } | |
| .input-wrapper { | |
| display: flex; | |
| gap: 12px; | |
| background: var(--message-ai-bg); | |
| border: 1px solid rgba(45, 212, 191, 0.2); | |
| padding: 12px 16px; | |
| border-radius: 12px; | |
| transition: all 0.3s ease; | |
| } | |
| .input-wrapper:focus-within { | |
| border-color: var(--primary-color); | |
| box-shadow: 0 0 15px rgba(45, 212, 191, 0.2); | |
| } | |
| #user-input { | |
| flex-grow: 1; | |
| padding: 8px 0; | |
| border: none; | |
| background: transparent; | |
| font-size: 16px; | |
| color: var(--text-primary); | |
| outline: none; | |
| resize: none; | |
| min-height: 24px; | |
| max-height: 200px; | |
| line-height: 1.5; | |
| } | |
| .send-button { | |
| background: var(--message-user-bg); | |
| border: none; | |
| border-radius: 8px; | |
| width: 32px; | |
| height: 32px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| padding: 0; | |
| margin: auto 0; | |
| box-shadow: var(--message-user-shadow); | |
| } | |
| .send-button:hover { | |
| transform: translateY(-2px) scale(1.05); | |
| box-shadow: 0 6px 20px rgba(45, 212, 191, 0.3); | |
| background: var(--accent-color); | |
| } | |
| .send-button:active { | |
| transform: translateY(0); | |
| } | |
| .send-button svg { | |
| width: 16px; | |
| height: 16px; | |
| stroke: white; | |
| } | |
| .send-button.disabled { | |
| background-color: var(--text-secondary); | |
| cursor: not-allowed; | |
| opacity: 0.5; | |
| } | |
| /* Keep existing code block and other styles... */ | |
| /* Code Block Styles within Messages */ | |
| .message .code-block { | |
| position: relative; | |
| background: #1e1e1e; | |
| border-radius: 8px; | |
| margin: 12px 0; | |
| font-family: 'Fira Code', 'Consolas', monospace; | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .code-block pre { | |
| margin: 0; | |
| padding: 16px; | |
| padding-top: 32px; /* Space for the copy button and language label */ | |
| overflow-x: auto; | |
| color: #e0e0e0; | |
| font-size: 14px; | |
| line-height: 1.5; | |
| } | |
| .code-block .copy-button { | |
| position: absolute; | |
| top: 8px; | |
| right: 8px; | |
| background: rgba(255, 255, 255, 0.1); | |
| color: #fff; | |
| border: none; | |
| border-radius: 4px; | |
| padding: 4px 8px; | |
| font-size: 12px; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| gap: 4px; | |
| transition: all 0.2s ease; | |
| opacity: 0; | |
| } | |
| .code-block:hover .copy-button { | |
| opacity: 1; | |
| } | |
| .code-block .copy-button:hover { | |
| background: rgba(255, 255, 255, 0.2); | |
| } | |
| .code-block .copy-button svg { | |
| width: 14px; | |
| height: 14px; | |
| } | |
| .code-block .language-label { | |
| position: absolute; | |
| top: 8px; | |
| left: 8px; | |
| color: #888; | |
| font-size: 12px; | |
| font-family: 'Inter', sans-serif; | |
| } | |
| /* Syntax Highlighting */ | |
| .code-block .keyword { color: #569CD6; } | |
| .code-block .string { color: #CE9178; } | |
| .code-block .comment { color: #6A9955; } | |
| .code-block .number { color: #B5CEA8; } | |
| .code-block .function { color: #DCDCAA; } | |
| .code-block .operator { color: #D4D4D4; } | |
| .code-block .class { color: #4EC9B0; } | |
| .code-block .property { color: #9CDCFE; } | |
| /* Thinking Animation Styles */ | |
| .thinking { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| } | |
| .loading-dots { | |
| display: flex; | |
| gap: 4px; | |
| } | |
| .loading-dots span { | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| background: var(--primary-color); | |
| animation: pulse 1.5s infinite ease-in-out; | |
| } | |
| .loading-dots span:nth-child(2) { animation-delay: 0.2s; } | |
| .loading-dots span:nth-child(3) { animation-delay: 0.4s; } | |
| @keyframes pulse { | |
| 0%, 100% { transform: scale(0.5); opacity: 0.5; } | |
| 50% { transform: scale(1); opacity: 1; } | |
| } | |
| /* Scrollbar styling */ | |
| ::-webkit-scrollbar { | |
| width: 6px; | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: transparent; | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: var(--primary-color); | |
| border-radius: 3px; | |
| } | |
| /* Add smooth animations */ | |
| @keyframes slideIn { | |
| from { | |
| transform: translateX(-100%); | |
| opacity: 0; | |
| } | |
| to { | |
| transform: translateX(0); | |
| opacity: 1; | |
| } | |
| } | |
| @keyframes messageSlide { | |
| from { | |
| transform: translateY(20px); | |
| opacity: 0; | |
| } | |
| to { | |
| transform: translateY(0); | |
| opacity: 1; | |
| } | |
| } | |
| /* Add glow effect to messages */ | |
| .message { | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .message.user-message::after { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: linear-gradient( | |
| 45deg, | |
| transparent, | |
| rgba(45, 212, 191, 0.1), | |
| transparent | |
| ); | |
| animation: shine 2s infinite; | |
| } | |
| @keyframes shine { | |
| 0% { | |
| transform: translateX(-100%) translateY(-100%); | |
| } | |
| 50%, 100% { | |
| transform: translateX(100%) translateY(100%); | |
| } | |
| } | |
| /* Add hover effect for avatars */ | |
| .avatar:hover { | |
| transform: scale(1.05); | |
| transition: transform 0.2s ease; | |
| } | |
| /* Scroll Buttons */ | |
| .scroll-button { | |
| position: fixed; | |
| right: 30px; | |
| width: 45px; | |
| height: 45px; | |
| border-radius: 50%; | |
| background: #2dd4bf; | |
| color: white; | |
| border: none; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| opacity: 0; | |
| visibility: hidden; | |
| transition: opacity 0.3s; | |
| box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); | |
| } | |
| .scroll-button.visible { | |
| opacity: 1; | |
| visibility: visible; | |
| } | |
| .scroll-top-button { | |
| bottom: 100px; | |
| } | |
| .scroll-bottom-button { | |
| bottom: 40px; | |
| } | |
| /* Add a glow effect to the buttons */ | |
| .scroll-button::after { | |
| content: ''; | |
| position: absolute; | |
| width: 100%; | |
| height: 100%; | |
| border-radius: 50%; | |
| background: inherit; | |
| filter: blur(8px); | |
| opacity: 0; | |
| transition: opacity 0.3s ease; | |
| z-index: -1; | |
| } | |
| .scroll-button:hover::after { | |
| opacity: 0.5; | |
| } | |
| /* Add smooth scrolling to the container */ | |
| .chat-messages { | |
| scroll-behavior: smooth; | |
| } | |
| /* Add a subtle animation for new messages */ | |
| @keyframes messageAppear { | |
| from { | |
| opacity: 0; | |
| transform: translateY(10px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| .message { | |
| animation: messageAppear 0.3s ease-out; | |
| } | |
| /* Add this to clear floats */ | |
| .chat-messages::after { | |
| content: ''; | |
| display: table; | |
| clear: both; | |
| } | |
| /* Responsive Design */ | |
| @media (max-width: 768px) { | |
| body { | |
| grid-template-columns: 1fr; /* Change to single column layout */ | |
| } | |
| .sidebar { | |
| position: fixed; | |
| left: -100%; | |
| top: 0; | |
| bottom: 0; | |
| width: 280px; | |
| z-index: 1000; | |
| transition: left 0.3s ease; | |
| padding: 12px; | |
| background: var(--secondary-color); | |
| backdrop-filter: blur(10px); | |
| box-shadow: none; | |
| } | |
| .sidebar.active { | |
| left: 0; | |
| box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); | |
| } | |
| .menu-button { | |
| position: fixed; | |
| left: 15px; | |
| top: 15px; | |
| z-index: 1001; | |
| background: var(--primary-color); | |
| border: none; | |
| border-radius: 8px; | |
| width: 36px; | |
| height: 36px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| color: white; | |
| } | |
| .chat-header { | |
| padding: 15px 70px; | |
| height: 50px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .chat-title { | |
| font-size: 16px; | |
| max-width: 80%; | |
| } | |
| .main-container { | |
| padding-top: 0; | |
| height: 100vh; | |
| margin-left: 0; | |
| width: 100%; | |
| position: relative; | |
| z-index: 1; | |
| } | |
| .message { | |
| max-width: 90%; | |
| padding: 10px 12px; | |
| } | |
| .input-container { | |
| padding: 12px; | |
| width: calc(100% - 24px); | |
| } | |
| .scroll-button { | |
| right: 10px; | |
| width: 40px; | |
| height: 40px; | |
| } | |
| /* Adjust code blocks for mobile */ | |
| .code-block { | |
| max-width: 100%; | |
| overflow-x: auto; | |
| } | |
| .code-block pre { | |
| padding: 12px; | |
| font-size: 12px; | |
| } | |
| .chat-list { | |
| padding-bottom: 20px; | |
| height: calc(100vh - 80px); | |
| overflow-y: auto; | |
| } | |
| .chat-item { | |
| padding: 12px 10px; | |
| font-size: 14px; | |
| margin-bottom: 4px; | |
| } | |
| .chat-item span { | |
| max-width: 200px; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| white-space: nowrap; | |
| } | |
| /* Adjust new chat button for mobile */ | |
| .new-chat-btn { | |
| margin: 0 0 12px 0; | |
| width: calc(100% - 24px); | |
| padding: 12px; | |
| font-size: 14px; | |
| } | |
| } | |
| /* Add overlay for mobile menu */ | |
| .overlay { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: rgba(0, 0, 0, 0.5); | |
| z-index: 999; | |
| } | |
| .overlay.active { | |
| display: block; | |
| } | |
| /* Enhanced Background */ | |
| body { | |
| background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 100%); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| /* Animated background elements */ | |
| .background-effects { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| z-index: 0; | |
| pointer-events: none; | |
| } | |
| .background-effects::before, | |
| .background-effects::after { | |
| content: ''; | |
| position: absolute; | |
| width: 300px; | |
| height: 300px; | |
| border-radius: 50%; | |
| filter: blur(100px); | |
| opacity: 0.15; | |
| animation: floatAround 20s infinite ease-in-out; | |
| } | |
| .background-effects::before { | |
| background: #00ffaa; | |
| top: -150px; | |
| left: -150px; | |
| } | |
| .background-effects::after { | |
| background: #00c8ff; | |
| bottom: -150px; | |
| right: -150px; | |
| animation-delay: -10s; | |
| } | |
| @keyframes floatAround { | |
| 0%, 100% { transform: translate(0, 0); } | |
| 25% { transform: translate(100px, 100px); } | |
| 50% { transform: translate(0, 200px); } | |
| 75% { transform: translate(-100px, 100px); } | |
| } | |
| /* Welcome Message Styles */ | |
| .welcome-message { | |
| background: rgba(255, 255, 255, 0.05); | |
| border-radius: 12px; | |
| padding: 20px; | |
| margin: 10px 20px 24px 20px; | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| animation: fadeIn 0.5s ease-out; | |
| max-width: calc(100% - 40px); | |
| } | |
| .welcome-message h3 { | |
| color: var(--primary-color); | |
| margin: 0 0 12px 0; | |
| font-size: 1.2rem; | |
| } | |
| .welcome-message p { | |
| margin: 0 0 16px 0; | |
| line-height: 1.5; | |
| font-size: 0.95rem; | |
| } | |
| .quick-actions { | |
| display: flex; | |
| gap: 8px; | |
| flex-wrap: wrap; | |
| margin-top: 16px; | |
| } | |
| .quick-action-btn { | |
| background: rgba(255, 255, 255, 0.1); | |
| border: none; | |
| padding: 8px 16px; | |
| border-radius: 20px; | |
| color: var(--text-primary); | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| font-size: 14px; | |
| white-space: nowrap; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .quick-action-btn:hover { | |
| background: var(--primary-color); | |
| transform: translateY(-2px); | |
| box-shadow: 0 4px 12px rgba(45, 212, 191, 0.3); | |
| } | |
| .quick-action-btn:active { | |
| transform: translateY(0); | |
| } | |
| /* Add ripple effect */ | |
| .quick-action-btn::after { | |
| content: ''; | |
| position: absolute; | |
| width: 100%; | |
| height: 100%; | |
| top: 0; | |
| left: 0; | |
| pointer-events: none; | |
| background-image: radial-gradient(circle, #fff 10%, transparent 10.01%); | |
| background-repeat: no-repeat; | |
| background-position: 50%; | |
| transform: scale(10, 10); | |
| opacity: 0; | |
| transition: transform .5s, opacity 1s; | |
| } | |
| .quick-action-btn:active::after { | |
| transform: scale(0, 0); | |
| opacity: .3; | |
| transition: 0s; | |
| } | |
| /* Add glow effect on hover */ | |
| .quick-action-btn:hover::before { | |
| content: ''; | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| border-radius: 20px; | |
| background: var(--primary-color); | |
| z-index: -1; | |
| filter: blur(8px); | |
| opacity: 0.5; | |
| transition: opacity 0.3s ease; | |
| } | |
| /* Mobile adjustments for hover effects */ | |
| @media (max-width: 768px) { | |
| .quick-action-btn { | |
| padding: 10px 16px; /* Slightly larger touch target */ | |
| } | |
| .quick-action-btn:active { | |
| background: var(--primary-color); | |
| transform: scale(0.98); | |
| } | |
| } | |
| /* Mobile Responsive Styles */ | |
| @media (max-width: 768px) { | |
| .welcome-message { | |
| margin: 10px 10px 20px 10px; | |
| padding: 15px; | |
| max-width: calc(100% - 20px); | |
| } | |
| .welcome-message h3 { | |
| font-size: 1.1rem; | |
| margin-bottom: 10px; | |
| } | |
| .welcome-message p { | |
| font-size: 0.9rem; | |
| margin-bottom: 12px; | |
| } | |
| .quick-actions { | |
| gap: 6px; | |
| margin-top: 12px; | |
| } | |
| .quick-action-btn { | |
| padding: 6px 12px; | |
| font-size: 13px; | |
| flex: 1 1 calc(50% - 6px); /* Two buttons per row on mobile */ | |
| text-align: center; | |
| min-width: calc(50% - 6px); | |
| max-width: calc(50% - 6px); | |
| } | |
| } | |
| /* Add fade in animation */ | |
| @keyframes fadeIn { | |
| from { | |
| opacity: 0; | |
| transform: translateY(10px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| /* Enhanced Input Area */ | |
| .input-container { | |
| position: relative; | |
| backdrop-filter: blur(10px); | |
| } | |
| .input-wrapper { | |
| position: relative; | |
| } | |
| .input-actions { | |
| position: absolute; | |
| right: 50px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| display: flex; | |
| gap: 8px; | |
| } | |
| .input-action-btn { | |
| background: none; | |
| border: none; | |
| color: var(--text-secondary); | |
| cursor: pointer; | |
| padding: 4px; | |
| transition: all 0.2s ease; | |
| } | |
| .input-action-btn:hover { | |
| color: var(--primary-color); | |
| } | |
| /* Settings Button */ | |
| .settings-button { | |
| position: absolute; | |
| right: 20px; | |
| top: 50%; | |
| transform: translateY(-50%); | |
| width: 40px; | |
| height: 40px; | |
| border-radius: 10px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: pointer; | |
| color: var(--text-secondary); | |
| transition: all 0.3s ease; | |
| background: rgba(255, 255, 255, 0.1); | |
| } | |
| .settings-button:hover { | |
| background: var(--primary-color); | |
| color: white; | |
| transform: translateY(-50%) scale(1.05); | |
| } | |
| /* Settings Modal */ | |
| .settings-modal { | |
| display: none; | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| right: 0; | |
| bottom: 0; | |
| background: rgba(0, 0, 0, 0.5); | |
| z-index: 1000; | |
| backdrop-filter: blur(5px); | |
| } | |
| .settings-modal.active { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| } | |
| .settings-content { | |
| background: var(--secondary-color); | |
| border-radius: 12px; | |
| width: 90%; | |
| max-width: 500px; | |
| position: relative; | |
| animation: modalSlide 0.3s ease; | |
| } | |
| .settings-header { | |
| padding: 20px; | |
| border-bottom: 1px solid var(--border-color); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .settings-header h2 { | |
| margin: 0; | |
| color: var(--text-primary); | |
| } | |
| .close-settings { | |
| background: none; | |
| border: none; | |
| color: var(--text-secondary); | |
| font-size: 24px; | |
| cursor: pointer; | |
| padding: 0; | |
| } | |
| .settings-body { | |
| padding: 20px; | |
| } | |
| .settings-section { | |
| margin-bottom: 24px; | |
| } | |
| .settings-section h3 { | |
| margin: 0 0 12px 0; | |
| color: var(--text-primary); | |
| } | |
| .theme-options { | |
| display: flex; | |
| gap: 8px; | |
| } | |
| .theme-btn { | |
| padding: 8px 16px; | |
| border-radius: 8px; | |
| border: 1px solid var(--border-color); | |
| background: transparent; | |
| color: var(--text-primary); | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| } | |
| .theme-btn.active { | |
| background: var(--primary-color); | |
| border-color: var(--primary-color); | |
| } | |
| .language-select { | |
| width: 100%; | |
| padding: 8px; | |
| border-radius: 8px; | |
| background: var(--bg-color); | |
| color: var(--text-primary); | |
| border: 1px solid var(--border-color); | |
| } | |
| @keyframes modalSlide { | |
| from { | |
| opacity: 0; | |
| transform: translateY(20px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| } | |
| /* Mobile Responsive */ | |
| @media (max-width: 768px) { | |
| .settings-button { | |
| right: 15px; | |
| width: 36px; | |
| height: 36px; | |
| } | |
| .settings-content { | |
| width: 95%; | |
| margin: 20px; | |
| } | |
| } | |
| /* Landing Page Styles */ | |
| .landing-page { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 100%); | |
| z-index: 9999; | |
| overflow-y: auto; | |
| transition: opacity 0.5s ease, transform 0.5s ease; | |
| -webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */ | |
| } | |
| .landing-page.hidden { | |
| opacity: 0; | |
| transform: translateY(-20px); | |
| pointer-events: none; | |
| } | |
| .landing-container { | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 40px 20px; | |
| } | |
| .hero-section { | |
| text-align: center; | |
| padding: 60px 0; | |
| animation: fadeIn 1s ease-out; | |
| } | |
| .hero-title { | |
| font-size: 3.5rem; | |
| margin-bottom: 20px; | |
| background: linear-gradient(135deg, #2dd4bf, #818cf8); | |
| -webkit-background-clip: text; | |
| background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| } | |
| .hero-subtitle { | |
| font-size: 1.2rem; | |
| color: var(--text-secondary); | |
| margin-bottom: 40px; | |
| } | |
| .features-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); | |
| gap: 30px; | |
| padding: 40px 0; | |
| } | |
| .feature-card { | |
| background: rgba(255, 255, 255, 0.05); | |
| border-radius: 15px; | |
| padding: 30px; | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| transition: transform 0.3s ease; | |
| } | |
| .feature-card:hover { | |
| transform: translateY(-5px); | |
| } | |
| .feature-icon { | |
| font-size: 2rem; | |
| margin-bottom: 20px; | |
| color: var(--primary-color); | |
| } | |
| .feature-title { | |
| font-size: 1.5rem; | |
| margin-bottom: 15px; | |
| color: var(--primary-color); | |
| } | |
| .feature-description { | |
| color: var(--text-secondary); | |
| } | |
| .cta-button { | |
| display: inline-block; | |
| background: linear-gradient(135deg, #2dd4bf, #818cf8); | |
| color: white; | |
| padding: 15px 40px; | |
| border-radius: 30px; | |
| text-decoration: none; | |
| font-weight: 600; | |
| font-size: 1.1rem; | |
| transition: all 0.3s ease; | |
| border: none; | |
| cursor: pointer; | |
| margin-top: 40px; | |
| } | |
| .cta-button:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 20px rgba(45, 212, 191, 0.2); | |
| } | |
| /* Mobile Responsive */ | |
| @media (max-width: 768px) { | |
| .hero-title { | |
| font-size: 2.5rem; | |
| } | |
| .hero-subtitle { | |
| font-size: 1rem; | |
| padding: 0 20px; | |
| } | |
| .features-grid { | |
| grid-template-columns: 1fr; | |
| padding: 20px; | |
| } | |
| .feature-card { | |
| padding: 20px; | |
| } | |
| } | |
| /* Update existing landing page styles */ | |
| .landing-page { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| background: linear-gradient(135deg, #0f172a 0%, #1e1b4b 100%); | |
| z-index: 9999; | |
| overflow-y: auto; | |
| transition: opacity 0.5s ease, transform 0.5s ease; | |
| -webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */ | |
| } | |
| /* Mobile responsive adjustments */ | |
| @media (max-width: 768px) { | |
| .landing-container { | |
| padding: 20px 15px; | |
| } | |
| .hero-section { | |
| padding: 40px 0; | |
| } | |
| .hero-title { | |
| font-size: 2.2rem; | |
| padding: 0 10px; | |
| margin-bottom: 15px; | |
| } | |
| .hero-subtitle { | |
| font-size: 1rem; | |
| padding: 0 15px; | |
| margin-bottom: 30px; | |
| } | |
| .features-grid { | |
| grid-template-columns: 1fr; | |
| gap: 15px; | |
| padding: 20px 0; | |
| } | |
| .feature-card { | |
| padding: 20px; | |
| margin: 0 10px; | |
| } | |
| .feature-title { | |
| font-size: 1.2rem; | |
| } | |
| .feature-description { | |
| font-size: 0.9rem; | |
| } | |
| .cta-button { | |
| padding: 12px 30px; | |
| font-size: 1rem; | |
| margin-top: 30px; | |
| width: 80%; | |
| max-width: 300px; | |
| } | |
| } | |
| /* Small mobile devices */ | |
| @media (max-width: 380px) { | |
| .hero-title { | |
| font-size: 1.8rem; | |
| } | |
| .feature-card { | |
| padding: 15px; | |
| } | |
| .cta-button { | |
| padding: 10px 25px; | |
| font-size: 0.9rem; | |
| } | |
| } | |
| /* Mobile responsive fixes */ | |
| @media (max-width: 768px) { | |
| body { | |
| grid-template-columns: 1fr; /* Single column layout for mobile */ | |
| } | |
| .main-container { | |
| width: 100%; | |
| height: 100vh; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .chat-messages { | |
| height: calc(100vh - 180px); /* Adjust for header and input */ | |
| padding: 10px; | |
| overflow-y: auto; | |
| -webkit-overflow-scrolling: touch; | |
| position: relative; | |
| margin-bottom: 60px; /* Space for input container */ | |
| } | |
| .welcome-message { | |
| position: relative; | |
| width: calc(100% - 20px); | |
| margin: 10px auto; | |
| z-index: 1; | |
| } | |
| .input-container { | |
| position: fixed; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| background: var(--bg-color); | |
| padding: 10px; | |
| border-top: 1px solid var(--border-color); | |
| z-index: 100; | |
| width: 100%; | |
| max-width: 100%; | |
| margin: 0; | |
| box-sizing: border-box; | |
| } | |
| .input-wrapper { | |
| max-width: 100%; | |
| margin: 0; | |
| } | |
| #user-input { | |
| font-size: 16px; /* Prevent zoom on mobile */ | |
| padding: 8px; | |
| } | |
| .message { | |
| max-width: 85%; | |
| margin-bottom: 10px; | |
| } | |
| /* Fix scroll buttons for mobile */ | |
| .scroll-button { | |
| width: 36px; | |
| height: 36px; | |
| right: 10px; | |
| z-index: 101; | |
| } | |
| .scroll-top-button { | |
| bottom: 80px; | |
| } | |
| .scroll-bottom-button { | |
| bottom: 130px; | |
| } | |
| /* Adjust chat header for mobile */ | |
| .chat-header { | |
| padding: 10px; | |
| height: auto; | |
| z-index: 99; | |
| } | |
| /* Quick action buttons adjustment */ | |
| .quick-actions { | |
| display: grid; | |
| grid-template-columns: repeat(2, 1fr); | |
| gap: 8px; | |
| padding: 0 5px; | |
| } | |
| .quick-action-btn { | |
| width: 100%; | |
| padding: 8px; | |
| font-size: 14px; | |
| text-align: center; | |
| white-space: normal; | |
| } | |
| } | |
| /* Small screen adjustments */ | |
| @media (max-width: 380px) { | |
| .chat-messages { | |
| height: calc(100vh - 160px); | |
| } | |
| .message { | |
| max-width: 90%; | |
| padding: 8px 12px; | |
| } | |
| .input-container { | |
| padding: 8px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="landing-page" id="landing-page"> | |
| <div class="landing-container"> | |
| <section class="hero-section"> | |
| <h1 class="hero-title">Welcome to Rxple AI</h1> | |
| <p class="hero-subtitle">Experience the future of AI-powered conversations with our advanced chatbot</p> | |
| </section> | |
| <div class="features-grid"> | |
| <div class="feature-card"> | |
| <div class="feature-icon">🤖</div> | |
| <h3 class="feature-title">Advanced AI Technology</h3> | |
| <p class="feature-description"> | |
| Powered by state-of-the-art language models, our AI understands context and provides accurate, relevant responses. | |
| </p> | |
| </div> | |
| <div class="feature-card"> | |
| <div class="feature-icon">💡</div> | |
| <h3 class="feature-title">Smart Assistance</h3> | |
| <p class="feature-description"> | |
| Get help with coding, writing, analysis, and more. Our AI adapts to your needs and learning style. | |
| </p> | |
| </div> | |
| <div class="feature-card"> | |
| <div class="feature-icon">🔒</div> | |
| <h3 class="feature-title">Secure & Private</h3> | |
| <p class="feature-description"> | |
| Your conversations are private and secure. We prioritize your data protection and privacy. | |
| </p> | |
| </div> | |
| <div class="feature-card"> | |
| <div class="feature-icon">⚡</div> | |
| <h3 class="feature-title">Real-time Responses</h3> | |
| <p class="feature-description"> | |
| Get instant answers to your questions with our lightning-fast response system. | |
| </p> | |
| </div> | |
| <div class="feature-card"> | |
| <div class="feature-icon">📱</div> | |
| <h3 class="feature-title">Mobile Friendly</h3> | |
| <p class="feature-description"> | |
| Access our AI assistant from any device with a seamless, responsive design. | |
| </p> | |
| </div> | |
| <div class="feature-card"> | |
| <div class="feature-icon">🔄</div> | |
| <h3 class="feature-title">Continuous Learning</h3> | |
| <p class="feature-description"> | |
| Our AI constantly improves and updates its knowledge to provide better assistance. | |
| </p> | |
| </div> | |
| </div> | |
| <section class="hero-section"> | |
| <button class="cta-button" onclick="startChat()">Start Chatting with AI</button> | |
| </section> | |
| </div> | |
| </div> | |
| <aside class="sidebar"> | |
| <button class="new-chat-btn" onclick="createNewChat()"> | |
| <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <line x1="12" y1="5" x2="12" y2="19"></line> | |
| <line x1="5" y1="12" x2="19" y2="12"></line> | |
| </svg> | |
| New Chat | |
| </button> | |
| <div class="chat-list" id="chat-list"> | |
| <!-- Chat items will be added here dynamically --> | |
| </div> | |
| </aside> | |
| <main class="main-container"> | |
| <div class="chat-header"> | |
| <div class="settings-button" id="settings-button" title="Settings"> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <path d="M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/> | |
| <path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/> | |
| </svg> | |
| </div> | |
| <h1 class="chat-title" id="current-chat-title">New Chat</h1> | |
| </div> | |
| <div class="chat-messages" id="chat-messages"></div> | |
| <button id="scroll-top" class="scroll-button scroll-top-button" title="Scroll to top"> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <path d="M18 15l-6-6-6 6"/> | |
| </svg> | |
| </button> | |
| <button id="scroll-bottom" class="scroll-button scroll-bottom-button" title="Scroll to bottom"> | |
| <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <path d="M6 9l6 6 6-6"/> | |
| </svg> | |
| </button> | |
| <div class="input-container"> | |
| <div class="input-wrapper"> | |
| <input | |
| type="text" | |
| id="user-input" | |
| placeholder="Send a message..." | |
| autocomplete="off" | |
| autofocus | |
| > | |
| <button class="send-button" id="send-button" onclick="sendMessage()"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <path d="M22 2L11 13M22 2L15 22L11 13L2 9L22 2Z"></path> | |
| </svg> | |
| </button> | |
| </div> | |
| </div> | |
| </main> | |
| <button class="menu-button" id="menu-button" style="display: none;"> | |
| <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <line x1="3" y1="12" x2="21" y2="12"></line> | |
| <line x1="3" y1="6" x2="21" y2="6"></line> | |
| <line x1="3" y1="18" x2="21" y2="18"></line> | |
| </svg> | |
| </button> | |
| <div class="overlay" id="overlay"></div> | |
| <div class="background-effects"></div> | |
| <div class="welcome-message"> | |
| <h3>👋 Welcome to AI Chat Assistant</h3> | |
| <p>I'm here to help you with any questions or tasks. Here are some things I can do:</p> | |
| <div class="quick-actions"> | |
| <button class="quick-action-btn" onclick="sendQuickAction('Tell me a joke')">Tell me a joke</button> | |
| <button class="quick-action-btn" onclick="sendQuickAction('Write a poem')">Write a poem</button> | |
| <button class="quick-action-btn" onclick="sendQuickAction('Explain quantum physics')">Explain quantum physics</button> | |
| <button class="quick-action-btn" onclick="sendQuickAction('Help with coding')">Help with coding</button> | |
| </div> | |
| </div> | |
| <div class="settings-modal" id="settings-modal"> | |
| <div class="settings-content"> | |
| <div class="settings-header"> | |
| <h2>Settings</h2> | |
| <button class="close-settings">×</button> | |
| </div> | |
| <div class="settings-body"> | |
| <div class="settings-section"> | |
| <h3>Theme</h3> | |
| <div class="theme-options"> | |
| <button class="theme-btn active" data-theme="dark">Dark</button> | |
| <button class="theme-btn" data-theme="light">Light</button> | |
| <button class="theme-btn" data-theme="system">System</button> | |
| </div> | |
| </div> | |
| <div class="settings-section"> | |
| <h3>Language</h3> | |
| <select class="language-select"> | |
| <option value="en">English</option> | |
| <option value="es">Español</option> | |
| <option value="fr">Français</option> | |
| </select> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Global variables | |
| const MISTRAL_API_KEY = 'UgKksYvcKnoxvJ13q5UBjL3AdU8Mu1DQ'; | |
| const chatMessages = document.getElementById('chat-messages'); | |
| const userInput = document.getElementById('user-input'); | |
| const sendButton = document.getElementById('send-button'); | |
| const scrollTopBtn = document.getElementById('scroll-top'); | |
| const scrollBottomBtn = document.getElementById('scroll-bottom'); | |
| let lastScrollTop = 0; | |
| let scrollCount = 0; | |
| let scrollTimeout; | |
| let chats = []; | |
| let currentChatId = null; | |
| // Initialize everything when the page loads | |
| document.addEventListener('DOMContentLoaded', () => { | |
| initializeScrollButtons(); | |
| // Only create one initial chat if no chats exist | |
| if (chats.length === 0) { | |
| createNewChat(); | |
| } | |
| // Add event listeners | |
| userInput.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter' && !e.shiftKey) { | |
| e.preventDefault(); | |
| sendMessage(); | |
| } | |
| }); | |
| userInput.addEventListener('input', () => { | |
| const isEmpty = userInput.value.trim() === ''; | |
| sendButton.classList.toggle('disabled', isEmpty); | |
| }); | |
| // Initialize send button state | |
| sendButton.classList.add('disabled'); | |
| }); | |
| // Remove all the duplicate function declarations and keep only one version of each: | |
| // - createNewChat | |
| // - updateChatList | |
| // - deleteChat | |
| // - switchChat | |
| // - updateChatTitle | |
| // - clearMessages | |
| // - formatMessage | |
| // - escapeHtml | |
| // - handleCopyClick | |
| // - sendMessage | |
| // - removeMessage | |
| // - initializeScrollButtons | |
| // - handleScroll | |
| // - scrollToTop | |
| // - scrollToBottom | |
| // Update the addMessage function to include both chat and scroll functionality | |
| function addMessage(text, sender, save = true) { | |
| const messageDiv = document.createElement('div'); | |
| messageDiv.className = `message ${sender}-message`; | |
| const avatar = document.createElement('div'); | |
| avatar.className = `avatar ${sender}-avatar`; | |
| avatar.textContent = sender === 'user' ? 'U' : 'AI'; | |
| const content = document.createElement('div'); | |
| content.className = 'message-content'; | |
| if (text === 'Thinking...') { | |
| content.innerHTML = ` | |
| <div class="thinking"> | |
| <span>Thinking</span> | |
| <div class="loading-dots"> | |
| <span></span> | |
| <span></span> | |
| <span></span> | |
| </div> | |
| </div>`; | |
| } else { | |
| content.innerHTML = formatMessage(text); | |
| // Add copy functionality to any code blocks | |
| content.querySelectorAll('.copy-button').forEach(button => { | |
| button.addEventListener('click', handleCopyClick); | |
| }); | |
| } | |
| messageDiv.appendChild(avatar); | |
| messageDiv.appendChild(content); | |
| const id = Date.now(); | |
| messageDiv.id = `message-${id}`; | |
| chatMessages.appendChild(messageDiv); | |
| // Save message to current chat | |
| if (save && currentChatId) { | |
| const chat = chats.find(c => c.id === currentChatId); | |
| if (chat) { | |
| chat.messages.push({ content: text, sender }); | |
| // Auto-generate title from first user message | |
| if (chat.title === 'New Chat' && sender === 'user') { | |
| const title = text.slice(0, 30) + (text.length > 30 ? '...' : ''); | |
| updateChatTitle(title); | |
| } | |
| } | |
| } | |
| // Improved scroll handling | |
| const shouldScroll = chatMessages.scrollTop + chatMessages.clientHeight >= chatMessages.scrollHeight - 100; | |
| // Wait for content to be rendered | |
| setTimeout(() => { | |
| if (shouldScroll) { | |
| chatMessages.scrollTo({ | |
| top: chatMessages.scrollHeight, | |
| behavior: 'smooth' | |
| }); | |
| } | |
| handleScroll(); | |
| }, 100); | |
| // Scroll to bottom after adding message | |
| setTimeout(() => { | |
| chatMessages.scrollTop = chatMessages.scrollHeight; | |
| }, 100); | |
| return id; | |
| } | |
| function formatMessage(text) { | |
| const codeBlockRegex = /```(\w*)\n([\s\S]*?)```/g; | |
| let formattedText = text; | |
| // Process code blocks | |
| formattedText = formattedText.replace(codeBlockRegex, (match, language, code) => { | |
| const highlightedCode = highlightSyntax(code.trim(), language); | |
| return ` | |
| <div class="code-block" data-language="${language || 'plaintext'}"> | |
| <span class="language-label">${language || 'plaintext'}</span> | |
| <pre><code>${highlightedCode}</code></pre> | |
| <button class="copy-button" data-code="${encodeURIComponent(code.trim())}"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <path d="M8 4v12a2 2 0 002 2h8a2 2 0 002-2V7.242a2 2 0 00-.602-1.43L16.083 2.57A2 2 0 0014.685 2H10a2 2 0 00-2 2z"/> | |
| <path d="M16 18v2a2 2 0 01-2 2H6a2 2 0 01-2-2V9a2 2 0 012-2h2"/> | |
| </svg> | |
| Copy | |
| </button> | |
| </div> | |
| `; | |
| }); | |
| return formattedText; | |
| } | |
| function highlightSyntax(code, language) { | |
| // Basic syntax highlighting | |
| return code | |
| .replace(/&/g, '&') | |
| .replace(/</g, '<') | |
| .replace(/>/g, '>') | |
| // Keywords | |
| .replace(/\b(function|const|let|var|if|else|return|class|import|export|default|new|this|for|while|break|continue)\b/g, '<span class="keyword">$1</span>') | |
| // Strings | |
| .replace(/(".*?"|'.*?'|`.*?`)/g, '<span class="string">$1</span>') | |
| // Comments | |
| .replace(/(\/\/.*|\/\*[\s\S]*?\*\/)/g, '<span class="comment">$1</span>') | |
| // Numbers | |
| .replace(/\b(\d+)\b/g, '<span class="number">$1</span>') | |
| // Functions | |
| .replace(/(\w+)\(/g, '<span class="function">$1</span>(') | |
| // Classes | |
| .replace(/\b([A-Z]\w*)\b/g, '<span class="class">$1</span>') | |
| // Properties | |
| .replace(/\.(\w+)\b/g, '.<span class="property">$1</span>'); | |
| } | |
| function handleCopyClick(event) { | |
| const button = event.target.closest('.copy-button'); | |
| if (!button) return; | |
| const code = decodeURIComponent(button.dataset.code); | |
| navigator.clipboard.writeText(code).then(() => { | |
| const originalText = button.innerHTML; | |
| button.innerHTML = ` | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <path d="M20 6L9 17l-5-5"/> | |
| </svg> | |
| Copied! | |
| `; | |
| button.style.background = '#10B981'; | |
| setTimeout(() => { | |
| button.innerHTML = originalText; | |
| button.style.background = ''; | |
| }, 2000); | |
| }).catch(err => { | |
| console.error('Failed to copy:', err); | |
| button.textContent = 'Failed'; | |
| }); | |
| } | |
| async function sendMessage() { | |
| const message = userInput.value.trim(); | |
| if (!message) return; | |
| try { | |
| addMessage(message, 'user'); | |
| userInput.value = ''; | |
| scrollToBottom(true); // Immediate scroll after user message | |
| const loadingId = addMessage('Thinking...', 'ai'); | |
| scrollToBottom(true); // Immediate scroll for loading message | |
| const response = await fetch('https://api.mistral.ai/v1/chat/completions', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Authorization': `Bearer ${MISTRAL_API_KEY}`, | |
| 'Accept': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| model: "mistral-small", | |
| messages: [ | |
| { | |
| role: "system", | |
| content: "You are a helpful AI assistant. Provide clear and concise responses." | |
| }, | |
| { | |
| role: "user", | |
| content: message | |
| } | |
| ], | |
| max_tokens: 1000, | |
| temperature: 0.7 | |
| }) | |
| }); | |
| if (!response.ok) { | |
| const errorData = await response.json().catch(() => ({})); | |
| throw new Error( | |
| `API Error (${response.status}): ${errorData.error?.message || 'Unknown error'}` | |
| ); | |
| } | |
| const data = await response.json(); | |
| removeMessage(loadingId); | |
| if (data.choices && data.choices[0] && data.choices[0].message) { | |
| removeMessage(loadingId); | |
| addMessage(data.choices[0].message.content, 'ai'); | |
| scrollToBottom(); // Smooth scroll for AI response | |
| } else { | |
| console.error('Unexpected API response:', data); | |
| addMessage('Sorry, I received an unexpected response format.', 'ai'); | |
| scrollToBottom(); | |
| } | |
| } catch (error) { | |
| console.error('Error details:', error); | |
| removeMessage(loadingId); | |
| addMessage(`Sorry, I encountered an error: ${error.message}`, 'ai'); | |
| scrollToBottom(); | |
| } finally { | |
| userInput.disabled = false; | |
| sendButton.classList.remove('disabled'); | |
| userInput.focus(); | |
| } | |
| } | |
| function removeMessage(id) { | |
| const message = document.getElementById(`message-${id}`); | |
| if (message) { | |
| message.remove(); | |
| } | |
| } | |
| function initializeScrollButtons() { | |
| const scrollTopBtn = document.getElementById('scroll-top'); | |
| const scrollBottomBtn = document.getElementById('scroll-bottom'); | |
| const chatMessages = document.getElementById('chat-messages'); | |
| chatMessages.addEventListener('scroll', () => { | |
| // Show top button when scrolled down | |
| if (chatMessages.scrollTop > 500) { | |
| scrollTopBtn.classList.add('visible'); | |
| } else { | |
| scrollTopBtn.classList.remove('visible'); | |
| } | |
| // Show bottom button when not at bottom | |
| const isAtBottom = chatMessages.scrollHeight - chatMessages.scrollTop - chatMessages.clientHeight < 100; | |
| if (!isAtBottom) { | |
| scrollBottomBtn.classList.add('visible'); | |
| } else { | |
| scrollBottomBtn.classList.remove('visible'); | |
| } | |
| }); | |
| // Scroll to top | |
| scrollTopBtn.addEventListener('click', () => { | |
| chatMessages.scrollTo({ top: 0, behavior: 'smooth' }); | |
| }); | |
| // Scroll to bottom | |
| scrollBottomBtn.addEventListener('click', () => { | |
| chatMessages.scrollTo({ top: chatMessages.scrollHeight, behavior: 'smooth' }); | |
| }); | |
| } | |
| function handleScroll() { | |
| const scrollTop = chatMessages.scrollTop; | |
| const scrollHeight = chatMessages.scrollHeight; | |
| const clientHeight = chatMessages.clientHeight; | |
| if (scrollTop > 100) { | |
| scrollTopBtn.classList.add('visible'); | |
| } else { | |
| scrollTopBtn.classList.remove('visible'); | |
| } | |
| if (scrollTop < lastScrollTop) { | |
| scrollCount++; | |
| if (scrollCount >= 2) { | |
| scrollBottomBtn.classList.add('visible'); | |
| } | |
| } else { | |
| scrollCount = 0; | |
| scrollBottomBtn.classList.remove('visible'); | |
| } | |
| clearTimeout(scrollTimeout); | |
| scrollTimeout = setTimeout(() => { | |
| scrollCount = 0; | |
| }, 1000); | |
| lastScrollTop = scrollTop; | |
| if (scrollHeight - scrollTop - clientHeight < 20) { | |
| scrollBottomBtn.classList.remove('visible'); | |
| } | |
| } | |
| function scrollToTop() { | |
| chatMessages.scrollTo({ | |
| top: 0, | |
| behavior: 'smooth' | |
| }); | |
| } | |
| function scrollToBottom(immediate = false) { | |
| const chatMessages = document.getElementById('chat-messages'); | |
| if (chatMessages) { | |
| const scrollOptions = { | |
| top: chatMessages.scrollHeight, | |
| behavior: immediate ? 'auto' : 'smooth' | |
| }; | |
| // For mobile Safari | |
| if (chatMessages.scrollTo) { | |
| chatMessages.scrollTo(scrollOptions); | |
| } else { | |
| chatMessages.scrollTop = chatMessages.scrollHeight; | |
| } | |
| } | |
| } | |
| // Initialize with a new chat | |
| createNewChat(); | |
| function createNewChat() { | |
| const chatId = Date.now(); | |
| const chat = { | |
| id: chatId, | |
| title: 'New Chat', | |
| messages: [] | |
| }; | |
| chats.push(chat); | |
| currentChatId = chatId; | |
| // Clear existing messages | |
| clearMessages(); | |
| // Update chat list and title | |
| updateChatList(); | |
| updateChatTitle('New Chat'); | |
| // Add welcome message for every new chat | |
| const welcomeMessage = document.createElement('div'); | |
| welcomeMessage.className = 'welcome-message'; | |
| welcomeMessage.innerHTML = ` | |
| <h3>👋 Welcome to Rxple AI Chat Assistant</h3> | |
| <p>I'm here to help you with any questions or tasks. Here are some things I can do:</p> | |
| <div class="quick-actions"> | |
| <button class="quick-action-btn" onclick="sendQuickAction('Tell me a joke')">Tell me a joke</button> | |
| <button class="quick-action-btn" onclick="sendQuickAction('Write a poem')">Write a poem</button> | |
| <button class="quick-action-btn" onclick="sendQuickAction('Explain quantum physics')">Explain quantum physics</button> | |
| <button class="quick-action-btn" onclick="sendQuickAction('Help with coding')">Help with coding</button> | |
| </div> | |
| `; | |
| chatMessages.appendChild(welcomeMessage); | |
| } | |
| function updateChatList() { | |
| const chatList = document.getElementById('chat-list'); | |
| chatList.innerHTML = ''; | |
| chats.forEach(chat => { | |
| const chatItem = document.createElement('div'); | |
| chatItem.className = `chat-item ${chat.id === currentChatId ? 'active' : ''}`; | |
| chatItem.innerHTML = ` | |
| <span>${chat.title}</span> | |
| <button class="delete-chat" onclick="deleteChat(${chat.id})"> | |
| <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <path d="M3 6h18"></path> | |
| <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"></path> | |
| <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path> | |
| </svg> | |
| </button> | |
| `; | |
| chatItem.onclick = (e) => { | |
| if (!e.target.closest('.delete-chat')) { | |
| switchChat(chat.id); | |
| } | |
| }; | |
| chatList.appendChild(chatItem); | |
| }); | |
| } | |
| function deleteChat(chatId) { | |
| if (confirm('Are you sure you want to delete this chat?')) { | |
| chats = chats.filter(chat => chat.id !== chatId); | |
| if (currentChatId === chatId) { | |
| if (chats.length > 0) { | |
| switchChat(chats[0].id); | |
| } else { | |
| createNewChat(); | |
| } | |
| } | |
| updateChatList(); | |
| } | |
| } | |
| function switchChat(chatId) { | |
| currentChatId = chatId; | |
| const chat = chats.find(c => c.id === chatId); | |
| clearMessages(); | |
| chat.messages.forEach(msg => { | |
| addMessage(msg.content, msg.sender, false); | |
| }); | |
| updateChatTitle(chat.title); | |
| updateChatList(); | |
| } | |
| function updateChatTitle(title) { | |
| document.getElementById('current-chat-title').textContent = title; | |
| if (currentChatId) { | |
| const chat = chats.find(c => c.id === currentChatId); | |
| if (chat) { | |
| chat.title = title; | |
| updateChatList(); | |
| } | |
| } | |
| } | |
| function clearMessages() { | |
| chatMessages.innerHTML = ''; | |
| } | |
| // Mobile menu functionality | |
| const menuButton = document.getElementById('menu-button'); | |
| const sidebar = document.querySelector('.sidebar'); | |
| const overlay = document.getElementById('overlay'); | |
| function checkScreenSize() { | |
| if (window.innerWidth <= 768) { | |
| menuButton.style.display = 'flex'; | |
| menuButton.style.opacity = sidebar.classList.contains('active') ? '0' : '1'; | |
| } else { | |
| menuButton.style.display = 'none'; | |
| menuButton.style.opacity = '1'; | |
| sidebar.classList.remove('active'); | |
| overlay.classList.remove('active'); | |
| } | |
| } | |
| menuButton.addEventListener('click', () => { | |
| sidebar.classList.toggle('active'); | |
| overlay.classList.toggle('active'); | |
| // Hide menu button when sidebar is open | |
| menuButton.style.opacity = sidebar.classList.contains('active') ? '0' : '1'; | |
| }); | |
| overlay.addEventListener('click', () => { | |
| sidebar.classList.remove('active'); | |
| overlay.classList.remove('active'); | |
| // Show menu button when sidebar is closed | |
| menuButton.style.opacity = '1'; | |
| }); | |
| // Check screen size on load and resize | |
| window.addEventListener('load', checkScreenSize); | |
| window.addEventListener('resize', checkScreenSize); | |
| // Close sidebar when clicking outside on mobile | |
| document.addEventListener('click', (e) => { | |
| if (window.innerWidth <= 768 && | |
| !e.target.closest('.sidebar') && | |
| !e.target.closest('.menu-button') && | |
| sidebar.classList.contains('active')) { | |
| sidebar.classList.remove('active'); | |
| overlay.classList.remove('active'); | |
| } | |
| }); | |
| // Update scroll button positions for mobile | |
| function updateScrollButtonPositions() { | |
| const inputHeight = document.querySelector('.input-container').offsetHeight; | |
| const scrollTopBtn = document.getElementById('scroll-top'); | |
| const scrollBottomBtn = document.getElementById('scroll-bottom'); | |
| if (window.innerWidth <= 768) { | |
| scrollBottomBtn.style.bottom = `${inputHeight + 20}px`; | |
| scrollTopBtn.style.bottom = `${inputHeight + 80}px`; | |
| } else { | |
| scrollBottomBtn.style.bottom = '40px'; | |
| scrollTopBtn.style.bottom = '100px'; | |
| } | |
| } | |
| // Update positions on load and resize | |
| window.addEventListener('load', updateScrollButtonPositions); | |
| window.addEventListener('resize', updateScrollButtonPositions); | |
| // Add transition to menu button in your styles | |
| const style = document.createElement('style'); | |
| style.textContent = ` | |
| .menu-button { | |
| transition: opacity 0.3s ease; | |
| } | |
| `; | |
| document.head.appendChild(style); | |
| function sendQuickAction(text) { | |
| userInput.value = text; | |
| sendMessage(); | |
| } | |
| // Settings functionality | |
| const settingsButton = document.getElementById('settings-button'); | |
| const settingsModal = document.getElementById('settings-modal'); | |
| const closeSettings = document.querySelector('.close-settings'); | |
| settingsButton.addEventListener('click', () => { | |
| settingsModal.classList.add('active'); | |
| }); | |
| closeSettings.addEventListener('click', () => { | |
| settingsModal.classList.remove('active'); | |
| }); | |
| // Close modal when clicking outside | |
| settingsModal.addEventListener('click', (e) => { | |
| if (e.target === settingsModal) { | |
| settingsModal.classList.remove('active'); | |
| } | |
| }); | |
| // Theme switching functionality | |
| const themeButtons = document.querySelectorAll('.theme-btn'); | |
| themeButtons.forEach(button => { | |
| button.addEventListener('click', () => { | |
| themeButtons.forEach(btn => btn.classList.remove('active')); | |
| button.classList.add('active'); | |
| // Add theme switching logic here | |
| }); | |
| }); | |
| function startChat() { | |
| document.getElementById('landing-page').classList.add('hidden'); | |
| // Remove the createNewChat() call here since it's already initialized | |
| } | |
| // Add this to handle mobile viewport height issues | |
| function setMobileHeight() { | |
| const vh = window.innerHeight * 0.01; | |
| document.documentElement.style.setProperty('--vh', `${vh}px`); | |
| } | |
| window.addEventListener('load', setMobileHeight); | |
| window.addEventListener('resize', setMobileHeight); | |
| </script> | |
| </body> | |
| </html> | |