File size: 9,332 Bytes
baa9ec5 e2537d9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Local LLM Chat</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/animejs/lib/anime.iife.min.js"></script>
<style>
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.message-animation {
animation: fadeIn 0.3s ease-out forwards;
}
.chat-container {
height: calc(100vh - 160px);
}
.typing-indicator::after {
content: '...';
animation: typing 1.5s infinite;
}
@keyframes typing {
0% { content: '.'; }
33% { content: '..'; }
66% { content: '...'; }
}
</style>
</head>
<body class="bg-gray-50 text-gray-800 font-sans">
<div class="max-w-4xl mx-auto h-screen flex flex-col">
<!-- Header -->
<header class="bg-white shadow-sm py-4 px-6 flex items-center justify-between">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 rounded-full bg-gradient-to-r from-purple-500 to-blue-500 flex items-center justify-center">
<i data-feather="cpu" class="text-white"></i>
</div>
<h1 class="text-xl font-semibold">Local LLM Chat</h1>
</div>
<button class="p-2 rounded-full hover:bg-gray-100 transition-colors">
<i data-feather="settings" class="text-gray-500"></i>
</button>
</header>
<!-- Chat Area -->
<div class="chat-container overflow-y-auto p-6 space-y-4 flex-1">
<!-- Welcome Message -->
<div class="flex justify-start" data-aos="fade-right">
<div class="max-w-xs md:max-w-md lg:max-w-lg bg-white rounded-2xl p-4 shadow-sm">
<div class="flex items-start space-x-2">
<div class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
<i data-feather="cpu" class="text-gray-600 w-4 h-4"></i>
</div>
<div>
<p class="text-sm font-medium text-gray-700">Local LLM</p>
<p class="text-gray-600 mt-1">Hello! I'm your local AI assistant. How can I help you today?</p>
</div>
</div>
</div>
</div>
<!-- Example User Message -->
<div class="flex justify-end" data-aos="fade-left">
<div class="max-w-xs md:max-w-md lg:max-w-lg bg-blue-500 text-white rounded-2xl p-4 shadow-sm">
<p>What can you do?</p>
</div>
</div>
<!-- Example AI Response -->
<div class="flex justify-start">
<div class="max-w-xs md:max-w-md lg:max-w-lg bg-white rounded-2xl p-4 shadow-sm">
<div class="flex items-start space-x-2">
<div class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
<i data-feather="cpu" class="text-gray-600 w-4 h-4"></i>
</div>
<div>
<p class="text-sm font-medium text-gray-700">Local LLM</p>
<p class="text-gray-600 mt-1">I can answer questions, help with creative writing, summarize text, and much more - all running locally on your machine for privacy.</p>
</div>
</div>
</div>
</div>
<!-- Typing Indicator (hidden by default) -->
<div id="typing-indicator" class="flex justify-start hidden">
<div class="max-w-xs md:max-w-md lg:max-w-lg bg-white rounded-2xl p-4 shadow-sm">
<div class="flex items-start space-x-2">
<div class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
<i data-feather="cpu" class="text-gray-600 w-4 h-4"></i>
</div>
<div>
<p class="text-sm font-medium text-gray-700">Local LLM</p>
<p class="text-gray-600 mt-1 typing-indicator">Thinking</p>
</div>
</div>
</div>
</div>
</div>
<!-- Input Area -->
<div class="bg-white border-t border-gray-200 p-4">
<form id="chat-form" class="flex items-center space-x-2">
<div class="flex-1 relative">
<input
id="message-input"
type="text"
placeholder="Type your message..."
class="w-full px-4 py-3 rounded-full border border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent transition-all"
autocomplete="off"
>
<button type="button" class="absolute right-3 top-3 text-gray-400 hover:text-blue-500">
<i data-feather="paperclip"></i>
</button>
</div>
<button
type="submit"
class="w-12 h-12 rounded-full bg-gradient-to-r from-purple-500 to-blue-500 text-white flex items-center justify-center hover:opacity-90 transition-opacity"
>
<i data-feather="send"></i>
</button>
</form>
<p class="text-xs text-gray-500 mt-2 text-center">Your conversations stay on your device</p>
</div>
</div>
<script>
feather.replace();
// Simple chat functionality
const chatForm = document.getElementById('chat-form');
const messageInput = document.getElementById('message-input');
const chatContainer = document.querySelector('.chat-container');
const typingIndicator = document.getElementById('typing-indicator');
chatForm.addEventListener('submit', function(e) {
e.preventDefault();
const message = messageInput.value.trim();
if (!message) return;
// Add user message
addMessage(message, 'user');
messageInput.value = '';
// Show typing indicator
typingIndicator.classList.remove('hidden');
chatContainer.scrollTop = chatContainer.scrollHeight;
// Simulate AI response after delay
setTimeout(() => {
typingIndicator.classList.add('hidden');
addMessage(getRandomResponse(), 'ai');
}, 1500 + Math.random() * 2000);
});
function addMessage(text, sender) {
const messageDiv = document.createElement('div');
messageDiv.classList.add('flex', 'message-animation');
if (sender === 'user') {
messageDiv.classList.add('justify-end');
messageDiv.innerHTML = `
<div class="max-w-xs md:max-w-md lg:max-w-lg bg-blue-500 text-white rounded-2xl p-4 shadow-sm">
<p>${text}</p>
</div>
`;
} else {
messageDiv.classList.add('justify-start');
messageDiv.innerHTML = `
<div class="max-w-xs md:max-w-md lg:max-w-lg bg-white rounded-2xl p-4 shadow-sm">
<div class="flex items-start space-x-2">
<div class="w-8 h-8 rounded-full bg-gray-200 flex items-center justify-center">
<i data-feather="cpu" class="text-gray-600 w-4 h-4"></i>
</div>
<div>
<p class="text-sm font-medium text-gray-700">Local LLM</p>
<p class="text-gray-600 mt-1">${text}</p>
</div>
</div>
</div>
`;
}
chatContainer.appendChild(messageDiv);
chatContainer.scrollTop = chatContainer.scrollHeight;
feather.replace();
}
function getRandomResponse() {
const responses = [
"That's an interesting question. Based on my training data, I would say...",
"I can help with that. Here's what I know about this topic...",
"Great question! The answer depends on several factors...",
"I'm glad you asked. This is a complex topic, but in summary...",
"Let me think about that for a moment. My analysis suggests..."
];
return responses[Math.floor(Math.random() * responses.length)];
}
</script>
</body>
</html>
|