twist / index.html
protae5544's picture
Update index.html
4ec29e8 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="vite">
<title>AI Chatbot with HuggingFace API - Enhanced</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<style>
:root {
--bg-primary: #0a0a0a;
--bg-secondary: #1a1a1a;
--bg-tertiary: #2a2a2a;
--text-primary: #ffffff;
--text-secondary: #b0b0b0;
--accent-primary: #00d4ff;
--accent-secondary: #ff6b35;
--accent-tertiary: #7c4dff;
--error: #ff4757;
--success: #2ed573;
--warning: #ffa502;
--glass-bg: rgba(255, 255, 255, 0.05);
--glass-border: rgba(255, 255, 255, 0.1);
--shadow-lg: 0 20px 40px rgba(0, 0, 0, 0.6);
--shadow-xl: 0 25px 50px rgba(0, 0, 0, 0.8);
--gradient-primary: linear-gradient(135deg, var(--accent-primary), var(--accent-tertiary));
--gradient-secondary: linear-gradient(135deg, var(--accent-secondary), var(--accent-primary));
--gradient-bg: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Inter', sans-serif;
background: var(--bg-primary);
background-image:
radial-gradient(ellipse at top left, rgba(0, 212, 255, 0.05) 0%, transparent 50%),
radial-gradient(ellipse at bottom right, rgba(124, 77, 255, 0.05) 0%, transparent 50%);
color: var(--text-primary);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
overflow: hidden;
-webkit-tap-highlight-color: transparent;
backdrop-filter: blur(10px);
}
.app-container {
width: 95%;
max-width: 1400px;
background: var(--glass-bg);
backdrop-filter: blur(20px);
border: 1px solid var(--glass-border);
border-radius: 20px;
box-shadow: var(--shadow-xl);
display: flex;
overflow: hidden;
height: 90vh;
position: relative;
animation: slideIn 0.8s cubic-bezier(0.23, 1, 0.32, 1);
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(50px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.settings-panel {
width: 320px;
padding: 24px;
background: var(--bg-secondary);
backdrop-filter: blur(15px);
border-right: 1px solid var(--glass-border);
transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1);
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--accent-primary) transparent;
}
.settings-panel::-webkit-scrollbar {
width: 6px;
}
.settings-panel::-webkit-scrollbar-track {
background: transparent;
}
.settings-panel::-webkit-scrollbar-thumb {
background: var(--accent-primary);
border-radius: 3px;
}
.settings-panel.collapsed {
width: 0;
padding: 0;
overflow: hidden;
}
.main-content {
flex: 1;
display: flex;
flex-direction: column;
position: relative;
background: var(--bg-primary);
}
.form-field {
margin-bottom: 20px;
animation: fadeInUp 0.6s cubic-bezier(0.23, 1, 0.32, 1);
}
.form-field label {
display: block;
margin-bottom: 8px;
font-size: 1rem;
font-weight: 600;
color: var(--text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.form-field select, .form-field textarea {
width: 100%;
padding: 12px 16px;
border: 2px solid transparent;
border-radius: 12px;
background: var(--bg-tertiary);
color: var(--text-primary);
font-size: 1rem;
transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
backdrop-filter: blur(10px);
}
.form-field select:focus, .form-field textarea:focus {
outline: none;
border-color: var(--accent-primary);
box-shadow: 0 0 20px rgba(0, 212, 255, 0.3);
transform: translateY(-2px);
}
.form-field textarea {
resize: vertical;
min-height: 100px;
}
button {
padding: 12px 24px;
border: none;
border-radius: 12px;
background: var(--gradient-primary);
color: var(--text-primary);
cursor: pointer;
font-size: 1rem;
font-weight: 600;
transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
touch-action: manipulation;
position: relative;
overflow: hidden;
}
button::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);
transition: left 0.5s;
}
button:hover::before {
left: 100%;
}
button:hover {
transform: translateY(-3px);
box-shadow: 0 10px 25px rgba(0, 212, 255, 0.4);
}
button:active {
transform: translateY(-1px);
}
.material-icons {
font-family: 'Material Icons';
font-size: 28px;
vertical-align: middle;
}
.chat-container {
flex: 1;
padding: 24px;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 16px;
scrollbar-width: thin;
scrollbar-color: var(--accent-primary) transparent;
}
.chat-container::-webkit-scrollbar {
width: 8px;
}
.chat-container::-webkit-scrollbar-track {
background: transparent;
}
.chat-container::-webkit-scrollbar-thumb {
background: var(--accent-primary);
border-radius: 4px;
}
.message {
max-width: 80%;
padding: 16px 20px;
border-radius: 20px;
font-size: 1rem;
line-height: 1.6;
position: relative;
animation: messageSlide 0.5s cubic-bezier(0.23, 1, 0.32, 1);
backdrop-filter: blur(10px);
}
@keyframes messageSlide {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.message.user {
background: var(--gradient-primary);
align-self: flex-end;
box-shadow: 0 8px 20px rgba(0, 212, 255, 0.3);
}
.message.ai {
background: var(--glass-bg);
border: 1px solid var(--glass-border);
align-self: flex-start;
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
}
.input-container {
display: flex;
gap: 12px;
padding: 20px;
background: var(--glass-bg);
backdrop-filter: blur(20px);
border-top: 1px solid var(--glass-border);
align-items: center;
}
#userInput {
flex: 1;
padding: 16px 20px;
border: 2px solid transparent;
border-radius: 20px;
background: var(--bg-secondary);
color: var(--text-primary);
resize: none;
min-height: 60px;
max-height: 150px;
font-size: 1rem;
transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
backdrop-filter: blur(10px);
}
#userInput:focus {
outline: none;
border-color: var(--accent-primary);
box-shadow: 0 0 25px rgba(0, 212, 255, 0.3);
transform: translateY(-2px);
}
#sendButton, #attachButton {
display: flex;
align-items: center;
justify-content: center;
width: 60px;
height: 60px;
border-radius: 50%;
background: var(--gradient-primary);
transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
}
#sendButton:hover, #attachButton:hover {
transform: translateY(-3px) scale(1.05);
box-shadow: 0 12px 30px rgba(0, 212, 255, 0.5);
}
#sendButton:disabled {
background: var(--bg-tertiary);
cursor: not-allowed;
transform: none;
box-shadow: none;
}
.spinner {
width: 28px;
height: 28px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-top: 3px solid var(--accent-primary);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.notification {
position: fixed;
top: 24px;
right: 24px;
padding: 16px 24px;
border-radius: 12px;
color: #fff;
font-size: 1rem;
font-weight: 600;
z-index: 1000;
display: none;
backdrop-filter: blur(15px);
animation: notificationSlide 0.5s cubic-bezier(0.23, 1, 0.32, 1);
}
@keyframes notificationSlide {
from {
opacity: 0;
transform: translateX(100px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.error-message {
background: linear-gradient(135deg, var(--error), #ff6b6b);
box-shadow: 0 10px 25px rgba(255, 71, 87, 0.4);
}
.success-message {
background: linear-gradient(135deg, var(--success), #5af78e);
box-shadow: 0 10px 25px rgba(46, 213, 115, 0.4);
}
.carousel-container {
perspective: 1200px;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: relative;
user-select: none;
padding: 24px;
}
.carousel {
position: relative;
width: 90%;
max-width: 500px;
height: 300px;
transform-style: preserve-3d;
transition: transform 0.6s cubic-bezier(0.23, 1, 0.32, 1);
cursor: grab;
}
.carousel.dragging {
cursor: grabbing;
}
.carousel-card {
position: absolute;
width: 100%;
height: 100%;
background: var(--glass-bg);
backdrop-filter: blur(20px);
border: 1px solid var(--glass-border);
border-radius: 16px;
padding: 24px;
box-shadow: var(--shadow-lg);
display: flex;
flex-direction: column;
gap: 12px;
transition: all 0.6s cubic-bezier(0.23, 1, 0.32, 1);
}
.carousel-controls, .carousel-indicator {
display: flex;
gap: 16px;
margin-top: 24px;
}
.carousel-dot {
width: 14px;
height: 14px;
background: var(--bg-tertiary);
border-radius: 50%;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
border: 2px solid transparent;
}
.carousel-dot.active {
background: var(--accent-primary);
box-shadow: 0 0 20px rgba(0, 212, 255, 0.5);
transform: scale(1.2);
}
.carousel-dot:hover {
transform: scale(1.1);
border-color: var(--accent-primary);
}
.mode-toggle {
display: flex;
background: var(--bg-secondary);
backdrop-filter: blur(15px);
border-radius: 20px;
padding: 4px;
position: relative;
margin-bottom: 20px;
border: 1px solid var(--glass-border);
}
.mode-toggle button {
flex: 1;
padding: 12px 20px;
background: none;
border: none;
color: var(--text-secondary);
cursor: pointer;
z-index: 1;
font-size: 1rem;
font-weight: 600;
border-radius: 16px;
transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
}
.mode-toggle button.active {
color: var(--text-primary);
}
.mode-toggle-slider {
position: absolute;
top: 4px;
left: 4px;
height: calc(100% - 8px);
background: var(--gradient-primary);
border-radius: 16px;
transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1);
box-shadow: 0 4px 15px rgba(0, 212, 255, 0.3);
}
.dropzone {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 212, 255, 0.1);
backdrop-filter: blur(20px);
border: 3px dashed var(--accent-primary);
display: none;
align-items: center;
justify-content: center;
color: var(--text-primary);
font-size: 1.4rem;
font-weight: 600;
z-index: 10;
border-radius: 20px;
}
.dropzone.active {
display: flex;
animation: pulse 2s infinite;
}
@keyframes pulse {
0%, 100% { opacity: 0.7; }
50% { opacity: 1; }
}
.file-name {
font-size: 1rem;
color: var(--accent-primary);
margin-left: 12px;
align-self: center;
max-width: 200px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-weight: 600;
}
.chip-container {
display: flex;
gap: 12px;
margin-bottom: 20px;
flex-wrap: wrap;
padding: 0 24px;
}
.chip {
padding: 10px 18px;
background: var(--glass-bg);
backdrop-filter: blur(15px);
border: 1px solid var(--glass-border);
border-radius: 20px;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
color: var(--text-secondary);
}
.chip:hover {
background: var(--accent-primary);
color: var(--text-primary);
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 212, 255, 0.4);
}
.floating-buttons {
position: absolute;
top: 20px;
right: 20px;
display: flex;
gap: 12px;
z-index: 100;
}
.floating-btn {
width: 48px;
height: 48px;
border-radius: 50%;
background: var(--glass-bg);
backdrop-filter: blur(15px);
border: 1px solid var(--glass-border);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
padding: 0;
}
.floating-btn:hover {
background: var(--accent-primary);
transform: translateY(-3px) scale(1.05);
box-shadow: 0 8px 25px rgba(0, 212, 255, 0.4);
}
.code-tools {
position: absolute;
top: 12px;
right: 12px;
display: flex;
gap: 8px;
}
.code-tools button {
padding: 6px 12px;
font-size: 0.85rem;
background: var(--glass-bg);
backdrop-filter: blur(10px);
border: 1px solid var(--glass-border);
}
pre {
position: relative;
background: var(--bg-secondary);
border: 1px solid var(--glass-border);
padding: 20px;
border-radius: 12px;
overflow-x: auto;
backdrop-filter: blur(10px);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
code {
font-family: 'Fira Code', 'Consolas', monospace;
font-size: 0.9rem;
}
.image-preview {
max-width: 100%;
max-height: 300px;
border-radius: 12px;
margin-top: 12px;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3);
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (max-width: 768px) {
.app-container {
flex-direction: column;
height: auto;
min-height: 100vh;
border-radius: 0;
width: 100%;
}
.settings-panel {
width: 100%;
max-height: 40vh;
border-right: none;
border-bottom: 1px solid var(--glass-border);
}
.settings-panel.collapsed {
max-height: 0;
}
.chat-container {
max-height: 50vh;
padding: 16px;
}
.input-container {
flex-wrap: wrap;
gap: 12px;
padding: 16px;
}
#userInput {
width: 100%;
margin-bottom: 12px;
min-height: 50px;
}
#sendButton, #attachButton {
width: 56px;
height: 56px;
}
.carousel {
width: 95%;
height: 250px;
}
.floating-buttons {
top: 12px;
right: 12px;
}
.floating-btn {
width: 44px;
height: 44px;
}
.material-icons {
font-size: 24px;
}
}
@media (max-width: 480px) {
.chip-container {
padding: 0 16px;
}
.chip {
font-size: 0.8rem;
padding: 8px 14px;
}
.form-field select, .form-field textarea, #userInput {
font-size: 16px;
}
}
</style>
</head>
<body>
<div class="app-container" id="appContainer">
<div class="settings-panel" id="settingsPanel">
<h2 style="font-size: 1.4rem; margin-bottom: 24px; background: var(--gradient-primary); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;">⚙️ Settings</h2>
<div class="form-field">
<label>🤖 Main Model:</label>
<select id="modelSelect">
<option value="Qwen/Qwen2.5-Coder-32B-Instruct">Qwen2.5-Coder 32B (Code/Text)</option>
<option value="Qwen/Qwen2-VL-72B-Instruct">Qwen2-VL 72B (Text/Image)</option>
<option value="microsoft/Florence-2-large">Florence-2 Large (Text/Image)</option>
<option value="mistralai/Mixtral-8x7B-Instruct-v0.1">Mixtral 8x7B (Text)</option>
<option value="meta-llama/Llama-2-70b-chat-hf">Llama-2 70B (Chat)</option>
</select>
<p style="font-size: 0.8rem; color: var(--text-secondary); margin-top: 8px;">
💡 Choose a model. Use VL or Florence for image processing.
</p>
</div>
<div class="form-field">
<label>👁️ OCR Model (for images):</label>
<select id="ocrModelSelect">
<option value="none">None</option>
<option value="scb10x/typhoon-v1.5x-72b-instruct">Typhoon 1.5x 72B</option>
<option value="microsoft/trocr-base-printed">TrOCR Base</option>
</select>
</div>
<button id="saveSettingsBtn">💾 Save Settings</button>
</div>
<div class="main-content">
<div class="floating-buttons">
<button id="settingsToggle" class="floating-btn">
<span class="material-icons">settings</span>
</button>
<button id="fullscreenToggle" class="floating-btn">
<span class="material-icons">fullscreen</span>
</button>
<button id="refreshBtn" class="floating-btn">
<span class="material-icons">refresh</span>
</button>
</div>
<div class="mode-toggle">
<button id="carouselModeBtn" class="active">🎛️ Prompt Editor</button>
<button id="chatModeBtn">💬 Chat</button>
<div class="mode-toggle-slider" id="modeToggleSlider"></div>
</div>
<div id="carouselMode">
<div class="carousel-container">
<div class="carousel" id="promptCarousel">
<div class="carousel-card" style="transform: rotateY(0deg) translateZ(500px);">
<label>🎯 Primary System Prompt:</label>
<textarea id="primarySystemPrompt" placeholder="Enter primary system prompt for the AI assistant...">You are a powerful AI assistant that excels at understanding code, images, and technical content. Provide clear, accurate, and helpful responses. Focus on practical solutions and detailed explanations.</textarea>
</div>
<div class="carousel-card" style="transform: rotateY(60deg) translateZ(500px); opacity: 0.8;">
<label>👁️ OCR System Prompt:</label>
<textarea id="ocrSystemPrompt" placeholder="Enter OCR system prompt for image text extraction...">Extract all text from the provided image clearly and accurately. Preserve formatting, structure, and layout when possible. If text is unclear, indicate uncertain parts.</textarea>
</div>
<div class="carousel-card" style="transform: rotateY(120deg) translateZ(500px); opacity: 0.8;">
<label>💻 Code Template:</label>
<textarea id="codeTemplate" placeholder="Enter code formatting template...">```javascript
// Enhanced code implementation
function solution() {
// Your optimized code here
return result;
}
```</textarea>
</div>
<div class="carousel-card" style="transform: rotateY(180deg) translateZ(500px); opacity: 0.8;">
<label>📋 Additional Instructions:</label>
<textarea id="additionalInstructions" placeholder="Enter additional instructions for the AI...">Always provide working, tested code examples. Include error handling and optimization suggestions. Explain complex concepts step by step. Use modern best practices.</textarea>
</div>
<div class="carousel-card" style="transform: rotateY(240deg) translateZ(500px); opacity: 0.8;">
<label>🚀 Prompt Prefix:</label>
<textarea id="promptPrefix" placeholder="Enter text to prepend to all prompts...">Please analyze the following request carefully and provide a comprehensive solution:</textarea>
</div>
<div class="carousel-card" style="transform: rotateY(300deg) translateZ(500px); opacity: 0.8;">
<label>✨ Prompt Suffix:</label>
<textarea id="promptSuffix" placeholder="Enter text to append to all prompts...">Ensure your response is complete, accurate, and includes practical examples where applicable.</textarea>
</div>
</div>
<div class="carousel-controls">
<button id="prevCard"><span class="material-icons">chevron_left</span></button>
<button id="nextCard"><span class="material-icons">chevron_right</span></button>
</div>
<div class="carousel-indicator" id="carouselIndicator">
<div class="carousel-dot active" data-index="0"></div>
<div class="carousel-dot" data-index="1"></div>
<div class="carousel-dot" data-index="2"></div>
<div class="carousel-dot" data-index="3"></div>
<div class="carousel-dot" data-index="4"></div>
<div class="carousel-dot" data-index="5"></div>
</div>
<button id="applyPromptBtn" style="margin-top: 24px;">✅ Apply Prompts & Switch to Chat</button>
</div>
</div>
<div id="chatMode" style="display: none;">
<div class="chip-container" id="quickPrompts">
<span class="chip" data-prompt="Explain this code in detail">🔍 Explain Code</span>
<span class="chip" data-prompt="Generate a complete function">⚡ Generate Function</span>
<span class="chip" data-prompt="Debug and fix this issue">🐛 Debug Code</span>
<span class="chip" data-prompt="Optimize this for performance">🚀 Optimize</span>
<span class="chip" data-prompt="Add error handling">🛡️ Error Handling</span>
<span class="chip" data-prompt="Convert to modern syntax">✨ Modernize</span>
</div>
<div class="chat-container" id="chatContainer"></div>
<div class="input-container">
<button id="attachButton"><span class="material-icons">attach_file</span></button>
<input type="file" id="fileInput" multiple accept="image/*" style="display: none;">
<span class="file-name" id="fileName"></span>
<textarea id="userInput" placeholder="Type your message here... (Shift+Enter for new line)"></textarea>
<button id="sendButton"><span class="material-icons">send</span></button>
</div>
<div class="dropzone" id="dropzone">📁 Drop files here to attach them</div>
</div>
<div class="notification error-message" id="errorMessage">
<span id="errorText"></span>
</div>
<div class="notification success-message" id="successMessage">
<span id="successText"></span>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js"></script>
<script>
// Global variables
let attachedFiles = [];
let currentCarouselIndex = 0;
let isLoading = false;
let isDragging = false;
// Initialize the application
document.addEventListener('DOMContentLoaded', function() {
loadSettings();
setupEventListeners();
setupCarousel();
setupDropzone();
hljs.highlightAll();
});
// Settings management
function loadSettings() {
const settings = {
model: localStorage.getItem('selectedModel') || 'Qwen/Qwen2.5-Coder-32B-Instruct',
ocrModel: localStorage.getItem('selectedOcrModel') || 'none',
primarySystemPrompt: localStorage.getItem('primarySystemPrompt') || 'You are a powerful AI assistant that excels at understanding code, images, and technical content. Provide clear, accurate, and helpful responses. Focus on practical solutions and detailed explanations.',
ocrSystemPrompt: localStorage.getItem('ocrSystemPrompt') || 'Extract all text from the provided image clearly and accurately. Preserve formatting, structure, and layout when possible. If text is unclear, indicate uncertain parts.',
codeTemplate: localStorage.getItem('codeTemplate') || '```javascript\n// Enhanced code implementation\nfunction solution() {\n // Your optimized code here\n return result;\n}\n```',
additionalInstructions: localStorage.getItem('additionalInstructions') || 'Always provide working, tested code examples. Include error handling and optimization suggestions. Explain complex concepts step by step. Use modern best practices.',
promptPrefix: localStorage.getItem('promptPrefix') || 'Please analyze the following request carefully and provide a comprehensive solution:',
promptSuffix: localStorage.getItem('promptSuffix') || 'Ensure your response is complete, accurate, and includes practical examples where applicable.'
};
document.getElementById('modelSelect').value = settings.model;
document.getElementById('ocrModelSelect').value = settings.ocrModel;
document.getElementById('primarySystemPrompt').value = settings.primarySystemPrompt;
document.getElementById('ocrSystemPrompt').value = settings.ocrSystemPrompt;
document.getElementById('codeTemplate').value = settings.codeTemplate;
document.getElementById('additionalInstructions').value = settings.additionalInstructions;
document.getElementById('promptPrefix').value = settings.promptPrefix;
document.getElementById('promptSuffix').value = settings.promptSuffix;
}
function saveSettings() {
localStorage.setItem('selectedModel', document.getElementById('modelSelect').value);
localStorage.setItem('selectedOcrModel', document.getElementById('ocrModelSelect').value);
localStorage.setItem('primarySystemPrompt', document.getElementById('primarySystemPrompt').value);
localStorage.setItem('ocrSystemPrompt', document.getElementById('ocrSystemPrompt').value);
localStorage.setItem('codeTemplate', document.getElementById('codeTemplate').value);
localStorage.setItem('additionalInstructions', document.getElementById('additionalInstructions').value);
localStorage.setItem('promptPrefix', document.getElementById('promptPrefix').value);
localStorage.setItem('promptSuffix', document.getElementById('promptSuffix').value);
showNotification('Settings saved successfully!', 'success');
}
// Event listeners setup
function setupEventListeners() {
// Mode toggle
document.getElementById('carouselModeBtn').addEventListener('click', () => switchMode('carousel'));
document.getElementById('chatModeBtn').addEventListener('click', () => switchMode('chat'));
// Settings
document.getElementById('saveSettingsBtn').addEventListener('click', saveSettings);
document.getElementById('settingsToggle').addEventListener('click', toggleSettings);
// Carousel controls
document.getElementById('prevCard').addEventListener('click', () => rotateCarousel(-1));
document.getElementById('nextCard').addEventListener('click', () => rotateCarousel(1));
document.getElementById('applyPromptBtn').addEventListener('click', applyPromptsAndSwitchToChat);
// Chat controls
document.getElementById('sendButton').addEventListener('click', sendMessage);
document.getElementById('attachButton').addEventListener('click', () => document.getElementById('fileInput').click());
document.getElementById('fileInput').addEventListener('change', handleFileSelect);
document.getElementById('userInput').addEventListener('keydown', handleKeyDown);
// Other controls
document.getElementById('fullscreenToggle').addEventListener('click', toggleFullscreen);
document.getElementById('refreshBtn').addEventListener('click', refreshApp);
// Quick prompts
document.querySelectorAll('.chip').forEach(chip => {
chip.addEventListener('click', (e) => {
const prompt = e.target.getAttribute('data-prompt');
document.getElementById('userInput').value = prompt;
document.getElementById('userInput').focus();
});
});
// Carousel dots
document.querySelectorAll('.carousel-dot').forEach(dot => {
dot.addEventListener('click', (e) => {
const index = parseInt(e.target.getAttribute('data-index'));
goToCarouselIndex(index);
});
});
}
// Mode switching
function switchMode(mode) {
const carouselMode = document.getElementById('carouselMode');
const chatMode = document.getElementById('chatMode');
const carouselBtn = document.getElementById('carouselModeBtn');
const chatBtn = document.getElementById('chatModeBtn');
const slider = document.getElementById('modeToggleSlider');
if (mode === 'carousel') {
carouselMode.style.display = 'block';
chatMode.style.display = 'none';
carouselBtn.classList.add('active');
chatBtn.classList.remove('active');
slider.style.width = '50%';
slider.style.left = '4px';
} else {
carouselMode.style.display = 'none';
chatMode.style.display = 'block';
carouselBtn.classList.remove('active');
chatBtn.classList.add('active');
slider.style.width = '50%';
slider.style.left = '50%';
}
}
// Carousel functionality
function setupCarousel() {
updateCarouselPosition();
}
function rotateCarousel(direction) {
const totalCards = 6;
currentCarouselIndex = (currentCarouselIndex + direction + totalCards) % totalCards;
updateCarouselPosition();
}
function goToCarouselIndex(index) {
currentCarouselIndex = index;
updateCarouselPosition();
}
function updateCarouselPosition() {
const carousel = document.getElementById('promptCarousel');
const cards = carousel.querySelectorAll('.carousel-card');
const dots = document.querySelectorAll('.carousel-dot');
cards.forEach((card, index) => {
const angle = (index - currentCarouselIndex) * 60;
const isActive = index === currentCarouselIndex;
card.style.transform = `rotateY(${angle}deg) translateZ(500px)`;
card.style.opacity = isActive ? '1' : '0.6';
card.style.zIndex = isActive ? '10' : '1';
});
dots.forEach((dot, index) => {
dot.classList.toggle('active', index === currentCarouselIndex);
});
}
function applyPromptsAndSwitchToChat() {
saveSettings();
switchMode('chat');
showNotification('Prompts applied successfully!', 'success');
}
// File handling
function setupDropzone() {
const dropzone = document.getElementById('dropzone');
const chatMode = document.getElementById('chatMode');
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
chatMode.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
['dragenter', 'dragover'].forEach(eventName => {
chatMode.addEventListener(eventName, () => dropzone.classList.add('active'), false);
});
['dragleave', 'drop'].forEach(eventName => {
chatMode.addEventListener(eventName, () => dropzone.classList.remove('active'), false);
});
chatMode.addEventListener('drop', handleDrop, false);
}
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
handleFiles(files);
}
function handleFileSelect(e) {
const files = e.target.files;
handleFiles(files);
}
function handleFiles(files) {
attachedFiles = Array.from(files);
updateFileDisplay();
}
function updateFileDisplay() {
const fileName = document.getElementById('fileName');
if (attachedFiles.length === 0) {
fileName.textContent = '';
} else if (attachedFiles.length === 1) {
fileName.textContent = attachedFiles[0].name;
} else {
fileName.textContent = `${attachedFiles.length} files selected`;
}
}
// Chat functionality
function handleKeyDown(e) {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
}
async function sendMessage() {
const userInput = document.getElementById('userInput');
const message = userInput.value.trim();
if (!message && attachedFiles.length === 0) return;
if (isLoading) return;
const chatContainer = document.getElementById('chatContainer');
// Add user message
if (message) {
addMessage(message, 'user');
}
// Add file previews
if (attachedFiles.length > 0) {
for (const file of attachedFiles) {
if (file.type.startsWith('image/')) {
const imageUrl = URL.createObjectURL(file);
addImageMessage(imageUrl, 'user');
}
}
}
userInput.value = '';
setLoading(true);
try {
const response = await callHuggingFaceAPI(message, attachedFiles);
addMessage(response, 'ai');
} catch (error) {
console.error('Error:', error);
addMessage('Sorry, there was an error processing your request. Please try again.', 'ai');
showNotification('Error: ' + error.message, 'error');
} finally {
setLoading(false);
attachedFiles = [];
updateFileDisplay();
}
chatContainer.scrollTop = chatContainer.scrollHeight;
}
async function callHuggingFaceAPI(message, files) {
const model = document.getElementById('modelSelect').value;
const primaryPrompt = document.getElementById('primarySystemPrompt').value;
const prefix = document.getElementById('promptPrefix').value;
const suffix = document.getElementById('promptSuffix').value;
let fullPrompt = `${primaryPrompt}\n\n${prefix}\n\n${message}\n\n${suffix}`;
// Handle OCR for images if needed
if (files.length > 0) {
const ocrModel = document.getElementById('ocrModelSelect').value;
if (ocrModel !== 'none') {
for (const file of files) {
if (file.type.startsWith('image/')) {
try {
const ocrText = await performOCR(file, ocrModel);
fullPrompt += `\n\nExtracted text from image: ${ocrText}`;
} catch (error) {
console.warn('OCR failed:', error);
}
}
}
}
}
const response = await fetch(`https://api-inference.huggingface.co/models/${model}`, {
method: 'POST',
headers: {
'Authorization': 'Bearer hf_your_token_here', // Replace with actual token
'Content-Type': 'application/json',
},
body: JSON.stringify({
inputs: fullPrompt,
parameters: {
max_new_tokens: 2048,
temperature: 0.7,
top_p: 0.9,
do_sample: true
}
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
if (data.error) {
throw new Error(data.error);
}
return data[0]?.generated_text || data.choices?.[0]?.message?.content || 'No response generated';
}
async function performOCR(file, ocrModel) {
const formData = new FormData();
formData.append('file', file);
const response = await fetch(`https://api-inference.huggingface.co/models/${ocrModel}`, {
method: 'POST',
headers: {
'Authorization': 'Bearer hf_your_token_here', // Replace with actual token
},
body: formData
});
if (!response.ok) {
throw new Error(`OCR failed: ${response.status}`);
}
const data = await response.json();
return data.generated_text || data.text || 'Could not extract text';
}
function addMessage(content, sender) {
const chatContainer = document.getElementById('chatContainer');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${sender}`;
if (sender === 'ai') {
messageDiv.innerHTML = marked.parse(content);
// Re-highlight code blocks
messageDiv.querySelectorAll('pre code').forEach(block => {
hljs.highlightElement(block);
addCodeTools(block.parentElement);
});
} else {
messageDiv.textContent = content;
}
chatContainer.appendChild(messageDiv);
chatContainer.scrollTop = chatContainer.scrollHeight;
}
function addImageMessage(imageUrl, sender) {
const chatContainer = document.getElementById('chatContainer');
const messageDiv = document.createElement('div');
messageDiv.className = `message ${sender}`;
const img = document.createElement('img');
img.src = imageUrl;
img.className = 'image-preview';
img.alt = 'Uploaded image';
messageDiv.appendChild(img);
chatContainer.appendChild(messageDiv);
chatContainer.scrollTop = chatContainer.scrollHeight;
}
function addCodeTools(preElement) {
const toolsDiv = document.createElement('div');
toolsDiv.className = 'code-tools';
const copyBtn = document.createElement('button');
copyBtn.textContent = 'Copy';
copyBtn.onclick = () => {
navigator.clipboard.writeText(preElement.textContent);
showNotification('Code copied to clipboard!', 'success');
};
toolsDiv.appendChild(copyBtn);
preElement.style.position = 'relative';
preElement.appendChild(toolsDiv);
}
function setLoading(loading) {
isLoading = loading;
const sendButton = document.getElementById('sendButton');
const spinner = sendButton.querySelector('.spinner');
const icon = sendButton.querySelector('.material-icons');
if (loading) {
if (!spinner) {
const spinnerDiv = document.createElement('div');
spinnerDiv.className = 'spinner';
sendButton.innerHTML = '';
sendButton.appendChild(spinnerDiv);
}
sendButton.disabled = true;
} else {
sendButton.innerHTML = '<span class="material-icons">send</span>';
sendButton.disabled = false;
}
}
// Utility functions
function toggleSettings() {
const settingsPanel = document.getElementById('settingsPanel');
settingsPanel.classList.toggle('collapsed');
}
function toggleFullscreen() {
if (!document.fullscreenElement) {
document.documentElement.requestFullscreen();
} else {
document.exitFullscreen();
}
}
function refreshApp() {
location.reload();
}
function showNotification(message, type) {
const notification = document.getElementById(type === 'error' ? 'errorMessage' : 'successMessage');
const textElement = document.getElementById(type === 'error' ? 'errorText' : 'successText');
textElement.textContent = message;
notification.style.display = 'block';
setTimeout(() => {
notification.style.display = 'none';
}, 3000);
}
</script>
</body>
</html>