anycoder-6f27afd0 / index.html
AI4U2's picture
Upload folder using huggingface_hub
085d65b verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LocalAI Nexus - Hot Chat & Generate</title>
<!-- Phosphor Icons -->
<script src="https://unpkg.com/@phosphor-icons/web@2.1.1/dist/phosphor.min.js"></script>
<style>
:root {
--bg-dark: #09090b;
--bg-panel: #161618;
--bg-input: #202023;
--accent-primary: #8b5cf6;
--accent-secondary: #ec4899;
--text-main: #e2e2e2;
--text-muted: #88888b;
--border: #2e2e31;
--glow: 0 0 20px rgba(139, 92, 246, 0.15);
--font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
* {
box-sizing: box-sizing;
margin: 0;
padding: 0;
scrollbar-width: thin;
scrollbar-color: var(--accent-primary) var(--bg-dark);
}
body {
background-color: var(--bg-dark);
color: var(--text-main);
font-family: var(--font-family);
height: 100vh;
display: flex;
overflow: hidden;
}
/* --- Sidebar --- */
.sidebar {
width: 280px;
background: var(--bg-panel);
border-right: 1px solid var(--border);
display: flex;
flex-direction: column;
padding: 1.5rem;
transition: transform 0.3s ease;
z-index: 10;
}
.brand {
font-size: 1.2rem;
font-weight: 700;
color: var(--text-main);
margin-bottom: 2rem;
display: flex;
align-items: center;
gap: 10px;
}
.brand i { color: var(--accent-primary); }
.system-status {
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--border);
padding: 1rem;
border-radius: 8px;
margin-bottom: 2rem;
font-size: 0.8rem;
}
.status-row {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
}
.status-val { color: var(--accent-secondary); font-weight: bold; }
.history-list {
flex-grow: 1;
overflow-y: auto;
}
.history-item {
padding: 10px;
border-radius: 6px;
color: var(--text-muted);
cursor: pointer;
transition: 0.2s;
display: flex;
align-items: center;
gap: 8px;
font-size: 0.9rem;
}
.history-item:hover {
background: rgba(255, 255, 255, 0.05);
color: var(--text-main);
}
/* --- Main Chat Area --- */
.main-chat {
flex-grow: 1;
display: flex;
flex-direction: column;
position: relative;
}
.chat-container {
flex-grow: 1;
padding: 2rem;
overflow-y: auto;
display: flex;
flex-direction: column;
gap: 1.5rem;
max-width: 900px;
width: 100%;
margin: 0 auto;
}
/* Messages */
.message {
display: flex;
gap: 1rem;
opacity: 0;
animation: fadeIn 0.3s forwards;
}
@keyframes fadeIn { to { opacity: 1; } }
.message.user {
justify-content: flex-end;
}
.bubble {
max-width: 70%;
padding: 1rem 1.5rem;
border-radius: 12px;
line-height: 1.5;
position: relative;
}
.message.ai .bubble {
background: transparent;
border: 1px solid var(--border);
color: var(--text-main);
}
.message.user .bubble {
background: linear-gradient(135deg, var(--accent-primary), var(--accent-secondary));
color: white;
box-shadow: var(--glow);
}
.message.ai .avatar {
width: 32px;
height: 32px;
background: var(--bg-panel);
border: 1px solid var(--border);
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
color: var(--accent-primary);
}
/* Generated Image Styling */
.generated-image-container {
margin-top: 10px;
border-radius: 8px;
overflow: hidden;
border: 1px solid var(--border);
position: relative;
}
.generated-image-container img {
width: 100%;
display: block;
transition: transform 0.3s;
}
.generated-image-container:hover img {
transform: scale(1.02);
}
/* --- Input Area --- */
.input-area-wrapper {
padding: 2rem;
background: var(--bg-dark);
border-top: 1px solid var(--border);
}
.input-container {
max-width: 900px;
margin: 0 auto;
background: var(--bg-input);
border: 1px solid var(--border);
border-radius: 16px;
padding: 10px;
display: flex;
flex-direction: column;
box-shadow: var(--glow);
transition: border-color 0.2s;
}
.input-container:focus-within {
border-color: var(--accent-primary);
}
.mode-toggle {
display: flex;
align-items: center;
gap: 8px;
padding: 5px 10px;
font-size: 0.8rem;
color: var(--text-muted);
cursor: pointer;
user-select: none;
}
.mode-toggle.active {
color: var(--accent-secondary);
}
.switch {
position: relative;
display: inline-block;
width: 20px;
height: 20px;
}
.switch input { opacity: 0; width: 0; height: 0; }
.slider {
position: absolute;
cursor: pointer;
top: 0; left: 0; right: 0; bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 20px;
}
.slider:before {
position: absolute;
content: "";
height: 16px;
width: 16px;
left: 2px;
bottom: 2px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: var(--accent-secondary);
}
input:checked + .slider:before {
transform: translateX(0px);
}
.text-input-row {
display: flex;
align-items: flex-end;
gap: 10px;
padding-top: 5px;
}
textarea {
flex-grow: 1;
background: transparent;
border: none;
color: var(--text-main);
resize: none;
height: 40px;
font-family: var(--font-family);
font-size: 1rem;
outline: none;
}
.action-btn {
background: var(--bg-panel);
border: 1px solid var(--border);
color: var(--text-main);
width: 40px;
height: 40px;
border-radius: 8px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: 0.2s;
}
.action-btn:hover {
background: var(--accent-primary);
border-color: var(--accent-primary);
}
.action-btn.send {
background: var(--accent-primary);
border: none;
color: white;
}
/* Loading Animation */
.typing-indicator {
display: flex;
gap: 5px;
padding: 10px;
}
.dot {
width: 6px;
height: 6px;
background: var(--text-muted);
border-radius: 50%;
animation: bounce 1.4s infinite ease-in-out both;
}
.dot:nth-child(1) { animation-delay: -0.32s; }
.dot:nth-child(2) { animation-delay: -0.16s; }
@keyframes bounce {
0%, 80%, 100% { transform: scale(0); }
40% { transform: scale(1); }
}
/* Responsive */
@media (max-width: 768px) {
.sidebar {
position: absolute;
transform: translateX(-100%);
height: 100%;
box-shadow: 10px 0 20px rgba(0,0,0,0.5);
}
.sidebar.open {
transform: translateX(0);
}
.mobile-menu-btn {
display: block;
position: absolute;
top: 15px;
left: 15px;
z-index: 20;
background: var(--bg-panel);
border: 1px solid var(--border);
color: white;
padding: 8px;
border-radius: 4px;
}
}
@media (min-width: 769px) {
.mobile-menu-btn { display: none; }
}
/* Header Link */
.top-link {
position: absolute;
top: 20px;
right: 20px;
z-index: 100;
background: rgba(0,0,0,0.5);
padding: 8px 12px;
border-radius: 8px;
border: 1px solid var(--border);
font-size: 0.8rem;
color: var(--text-muted);
text-decoration: none;
transition: 0.2s;
}
.top-link:hover {
color: var(--accent-primary);
border-color: var(--accent-primary);
}
</style>
</head>
<body>
<!-- Mobile Menu Toggle -->
<button class="mobile-menu-btn" onclick="toggleSidebar()">
<i class="ph ph-list"></i>
</button>
<!-- Built with Link -->
<a href="https://huggingface.co/spaces/akhaliq/anycoder" class="top-link">
Built with anycoder
</a>
<!-- Sidebar -->
<aside class="sidebar" id="sidebar">
<div class="brand">
<i class="ph-fill ph-brain"></i> LocalAI Nexus
</div>
<div class="system-status">
<div class="status-row">
<span>GPU Status</span>
<span class="status-val">RTX 4090</span>
</div>
<div class="status-row">
<span>VRAM Usage</span>
<span class="status-val">12GB / 24GB</span>
</div>
<div class="status-row">
<span>Model</span>
<span class="status-val">Llama-3-8B</span>
</div>
</div>
<div class="history-list">
<div class="history-item"><i class="ph ph-chat-circle-text"></i> Cyberpunk City</div>
<div class="history-item"><i class="ph ph-image"></i> Portrait of Cat</div>
<div class="history-item"><i class="ph ph-code"></i> Python Script</div>
<div class="history-item"><i class="ph ph-chat-circle-text"></i> Philosophy</div>
</div>
</aside>
<!-- Main Chat -->
<main class="main-chat">
<div class="chat-container" id="chat-container">
<!-- Initial Greeting -->
<div class="message ai">
<div class="avatar"><i class="ph ph-brain"></i></div>
<div class="bubble">
System initialized. I am running locally on your machine. I can generate text or create images based on your prompts. What would you like to create today?
</div>
</div>
</div>
<!-- Input Area -->
<div class="input-area-wrapper">
<div class="input-container">
<!-- Mode Toggle -->
<div class="mode-toggle" id="mode-toggle" onclick="toggleMode()">
<label class="switch">
<input type="checkbox" id="image-mode-check">
<span class="slider"></span>
</label>
<span>Image Generation Mode</span>
</div>
<!-- Text Input -->
<div class="text-input-row">
<textarea id="user-input" placeholder="Type a message..." onkeydown="handleEnter(event)"></textarea>
<button class="action-btn send" onclick="sendMessage()">
<i class="ph ph-paper-plane-right"></i>
</button>
</div>
</div>
</div>
</main>
<script>
let isImageMode = false;
const chatContainer = document.getElementById('chat-container');
const userInput = document.getElementById('user-input');
const modeToggle = document.getElementById('mode-toggle');
const imageCheck = document.getElementById('image-mode-check');
// Sidebar Toggle
function toggleSidebar() {
document.getElementById('sidebar').classList.toggle('open');
}
// Toggle Mode
function toggleMode() {
isImageMode = !isImageMode;
imageCheck.checked = isImageMode;
if(isImageMode) {
modeToggle.classList.add('active');
userInput.placeholder = "Describe the image you want to generate...";
} else {
modeToggle.classList.remove('active');
userInput.placeholder = "Type a message...";
}
}
// Handle Enter Key
function handleEnter(e) {
if(e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
sendMessage();
}
}
// Send Message Logic
function sendMessage() {
const text = userInput.value.trim();
if(!text) return;
// 1. Add User Message
addMessage(text, 'user');
userInput.value = '';
// 2. Show Loading State
const loadingId = addLoadingIndicator();
// 3. Simulate AI Delay and Response
setTimeout(() => {
removeLoadingIndicator(loadingId);
if (isImageMode) {
generateMockImage(text);
} else {
generateMockText(text);
}
}, 1500 + Math.random() * 1000);
}
// UI: Add Message Bubble
function addMessage(content, type, isImage = false) {
const msgDiv = document.createElement('div');
msgDiv.className = `message ${type}`;
if (type === 'ai') {
msgDiv.innerHTML = `
<div class="avatar"><i class="ph ph-brain"></i></div>
<div class="bubble">${content}</div>
`;
} else {
msgDiv.innerHTML = `
<div class="bubble">${content}</div>
`;
}
chatContainer.appendChild(msgDiv);
scrollToBottom();
}
// UI: Add Loading Indicator
function addLoadingIndicator() {
const id = 'loading-' + Date.now();
const msgDiv = document.createElement('div');
msgDiv.className = 'message ai';
msgDiv.id = id;
msgDiv.innerHTML = `
<div class="avatar"><i class="ph ph-brain"></i></div>
<div class="bubble">
<div class="typing-indicator">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
</div>
`;
chatContainer.appendChild(msgDiv);
scrollToBottom();
return id;
}
function removeLoadingIndicator(id) {
const el = document.getElementById(id);
if(el) el.remove();
}
function scrollToBottom() {
chatContainer.scrollTop = chatContainer.scrollHeight;
}
// --- Mock AI Logic ---
function generateMockText(text) {
// Simple heuristic responses
let response = "I've processed your request locally. ";
if(text.toLowerCase().includes("hello")) response += "Greetings! The local node is ready.";
else if(text.toLowerCase().includes("code")) response += "I can certainly help you write code. Please specify the language.";
else if(text.toLowerCase().includes("image") && !isImageMode) response += "You can switch to Image Mode using the toggle below to generate visuals.";
else response += "That's an interesting topic. My local GPU is processing the context...";
addMessage(response, 'ai');
}
function generateMockImage(prompt) {
// We use picsum.photos to simulate generation because we can't actually run Stable Diffusion in pure JS without a backend
// We add a random seed to make it look different every time
const seed = Math.floor(Math.random() * 1000);
const imageUrl = `https://picsum.photos/seed/${seed}/600/400`;
const msgDiv = document.createElement('div');
msgDiv.className = 'message ai';
msgDiv.innerHTML = `
<div class="avatar"><i class="ph ph-brain"></i></div>
<div class="bubble">
<div style="font-size: 0.8rem; color: var(--text-muted); margin-bottom: 5px;">Generated locally based on: "${prompt}"</div>
<div class="generated-image-container">
<img src="${imageUrl}" alt="Generated Image" loading="lazy">
</div>
<div style="margin-top: 8px; font-size: 0.8rem; display: flex; gap: 10px;">
<span><i class="ph ph-download-simple"></i> Save</span>
<span><i class="ph ph-share-network"></i> Share</span>
</div>
</div>
`;
chatContainer.appendChild(msgDiv);
scrollToBottom();
}
</script>
</body>
</html>