Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Transformers WebApp Studio</title> | |
| <!-- Importing FontAwesome for Icons --> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <!-- Importing Google Fonts --> | |
| <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=JetBrains+Mono:wght@400;700&display=swap" rel="stylesheet"> | |
| <style> | |
| :root { | |
| --bg-dark: #0f1117; | |
| --bg-panel: #161b22; | |
| --bg-input: #0d1117; | |
| --border: #30363d; | |
| --accent: #58a6ff; | |
| --accent-hover: #79c0ff; | |
| --text-main: #c9d1d9; | |
| --text-muted: #8b949e; | |
| --success: #238636; | |
| --danger: #da3633; | |
| --font-main: 'Inter', sans-serif; | |
| --font-code: 'JetBrains Mono', monospace; | |
| } | |
| * { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| outline: none; | |
| } | |
| body { | |
| font-family: var(--font-main); | |
| background-color: var(--bg-dark); | |
| color: var(--text-main); | |
| height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden; | |
| } | |
| /* --- Header --- */ | |
| header { | |
| height: 60px; | |
| background-color: var(--bg-panel); | |
| border-bottom: 1px solid var(--border); | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 0 20px; | |
| flex-shrink: 0; | |
| } | |
| .logo { | |
| font-weight: 700; | |
| font-size: 1.2rem; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| color: var(--text-main); | |
| } | |
| .logo i { | |
| color: var(--accent); | |
| } | |
| .header-links a { | |
| color: var(--accent); | |
| text-decoration: none; | |
| font-size: 0.9rem; | |
| transition: opacity 0.2s; | |
| } | |
| .header-links a:hover { | |
| opacity: 0.8; | |
| text-decoration: underline; | |
| } | |
| /* --- Main Layout --- */ | |
| .main-container { | |
| display: flex; | |
| flex: 1; | |
| overflow: hidden; | |
| } | |
| /* --- Sidebar (History) --- */ | |
| .sidebar { | |
| width: 280px; | |
| background-color: var(--bg-panel); | |
| border-right: 1px solid var(--border); | |
| display: flex; | |
| flex-direction: column; | |
| transition: transform 0.3s ease; | |
| z-index: 10; | |
| } | |
| .sidebar-header { | |
| padding: 15px; | |
| border-bottom: 1px solid var(--border); | |
| } | |
| .btn-new-chat { | |
| width: 100%; | |
| padding: 10px; | |
| background-color: var(--success); | |
| color: white; | |
| border: none; | |
| border-radius: 6px; | |
| cursor: pointer; | |
| font-weight: 600; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| transition: background 0.2s; | |
| } | |
| .btn-new-chat:hover { | |
| background-color: #2ea043; | |
| } | |
| .history-list { | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 10px; | |
| } | |
| .history-item { | |
| padding: 10px; | |
| border-radius: 6px; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| margin-bottom: 5px; | |
| color: var(--text-main); | |
| transition: background 0.2s; | |
| } | |
| .history-item:hover, .history-item.active { | |
| background-color: var(--border); | |
| } | |
| .history-name { | |
| font-size: 0.9rem; | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| flex: 1; | |
| margin-right: 8px; | |
| border: 1px solid transparent; | |
| padding: 2px; | |
| } | |
| .history-name:focus { | |
| border-color: var(--accent); | |
| background: var(--bg-input); | |
| } | |
| .history-actions i { | |
| color: var(--text-muted); | |
| margin-left: 5px; | |
| font-size: 0.8rem; | |
| } | |
| .history-actions i:hover { | |
| color: var(--text-main); | |
| } | |
| .sidebar-footer { | |
| padding: 15px; | |
| border-top: 1px solid var(--border); | |
| } | |
| .btn-import { | |
| width: 100%; | |
| padding: 8px; | |
| background: transparent; | |
| border: 1px dashed var(--text-muted); | |
| color: var(--text-muted); | |
| border-radius: 6px; | |
| cursor: pointer; | |
| } | |
| .btn-import:hover { | |
| border-color: var(--accent); | |
| color: var(--accent); | |
| } | |
| /* --- Chat Area --- */ | |
| .chat-area { | |
| flex: 1; | |
| display: flex; | |
| flex-direction: column; | |
| background-color: var(--bg-dark); | |
| position: relative; | |
| } | |
| .chat-header { | |
| padding: 15px 20px; | |
| border-bottom: 1px solid var(--border); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| background: rgba(22, 27, 34, 0.8); | |
| backdrop-filter: blur(10px); | |
| } | |
| .model-indicator { | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| font-weight: 600; | |
| } | |
| .badge { | |
| font-size: 0.75rem; | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| background: var(--border); | |
| color: var(--text-muted); | |
| } | |
| .chat-messages { | |
| flex: 1; | |
| overflow-y: auto; | |
| padding: 20px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 20px; | |
| } | |
| .message { | |
| max-width: 80%; | |
| line-height: 1.5; | |
| position: relative; | |
| animation: fadeIn 0.3s ease; | |
| } | |
| @keyframes fadeIn { | |
| from { opacity: 0; transform: translateY(10px); } | |
| to { opacity: 1; transform: translateY(0); } | |
| } | |
| .message.user { | |
| align-self: flex-end; | |
| } | |
| .message.ai { | |
| align-self: flex-start; | |
| } | |
| .bubble { | |
| padding: 12px 16px; | |
| border-radius: 12px; | |
| position: relative; | |
| } | |
| .message.user .bubble { | |
| background-color: var(--accent); | |
| color: white; | |
| border-bottom-right-radius: 2px; | |
| } | |
| .message.ai .bubble { | |
| background-color: var(--bg-panel); | |
| border: 1px solid var(--border); | |
| border-bottom-left-radius: 2px; | |
| color: var(--text-main); | |
| } | |
| /* Copy Paste on every field */ | |
| .msg-actions { | |
| position: absolute; | |
| top: -20px; | |
| right: 0; | |
| display: none; | |
| gap: 8px; | |
| } | |
| .message:hover .msg-actions { | |
| display: flex; | |
| } | |
| .action-btn { | |
| background: var(--bg-panel); | |
| border: 1px solid var(--border); | |
| color: var(--text-muted); | |
| padding: 2px 6px; | |
| border-radius: 4px; | |
| font-size: 0.75rem; | |
| cursor: pointer; | |
| display: flex; | |
| align-items: center; | |
| gap: 4px; | |
| } | |
| .action-btn:hover { | |
| color: var(--accent); | |
| border-color: var(--accent); | |
| } | |
| .input-area { | |
| padding: 20px; | |
| border-top: 1px solid var(--border); | |
| background-color: var(--bg-panel); | |
| } | |
| .input-wrapper { | |
| position: relative; | |
| max-width: 900px; | |
| margin: 0 auto; | |
| } | |
| .chat-input { | |
| width: 100%; | |
| background-color: var(--bg-input); | |
| border: 1px solid var(--border); | |
| border-radius: 8px; | |
| padding: 15px 50px 15px 15px; | |
| color: var(--text-main); | |
| font-family: var(--font-main); | |
| resize: none; | |
| min-height: 54px; | |
| max-height: 200px; | |
| } | |
| .chat-input:focus { | |
| border-color: var(--accent); | |
| } | |
| .send-btn { | |
| position: absolute; | |
| right: 10px; | |
| bottom: 10px; | |
| background: none; | |
| border: none; | |
| color: var(--text-muted); | |
| cursor: pointer; | |
| font-size: 1.2rem; | |
| } | |
| .send-btn:hover { | |
| color: var(--accent); | |
| } | |
| /* --- Right Panel (Models & Config) --- */ | |
| .right-panel { | |
| width: 320px; | |
| background-color: var(--bg-panel); | |
| border-left: 1px solid var(--border); | |
| display: flex; | |
| flex-direction: column; | |
| overflow-y: auto; | |
| transition: width 0.3s ease; | |
| } | |
| .panel-section { | |
| padding: 15px; | |
| border-bottom: 1px solid var(--border); | |
| } | |
| .panel-title { | |
| font-size: 0.85rem; | |
| text-transform: uppercase; | |
| color: var(--text-muted); | |
| margin-bottom: 10px; | |
| font-weight: 700; | |
| letter-spacing: 0.5px; | |
| } | |
| /* Model Cards */ | |
| .model-card { | |
| background: var(--bg-input); | |
| border: 1px solid var(--border); | |
| border-radius: 6px; | |
| padding: 10px; | |
| margin-bottom: 10px; | |
| cursor: pointer; | |
| transition: all 0.2s; | |
| } | |
| .model-card:hover { | |
| border-color: var(--text-muted); | |
| } | |
| .model-card.active { | |
| border-color: var(--accent); | |
| background: rgba(88, 166, 255, 0.1); | |
| } | |
| .model-header { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-bottom: 5px; | |
| } | |
| .model-name { | |
| font-weight: 600; | |
| font-size: 0.9rem; | |
| } | |
| .model-meta { | |
| font-size: 0.75rem; | |
| color: var(--text-muted); | |
| } | |
| /* Showcase Elements */ | |
| .showcase-stats { | |
| display: flex; | |
| gap: 10px; | |
| margin-top: 8px; | |
| } | |
| .stat { | |
| font-size: 0.7rem; | |
| background: var(--border); | |
| padding: 2px 4px; | |
| border-radius: 3px; | |
| } | |
| /* Download Section */ | |
| .download-controls { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 10px; | |
| } | |
| .form-group { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 4px; | |
| } | |
| .form-group label { | |
| font-size: 0.75rem; | |
| color: var(--text-muted); | |
| } | |
| .form-input { | |
| background: var(--bg-input); | |
| border: 1px solid var(--border); | |
| color: var(--text-main); | |
| padding: 6px; | |
| border-radius: 4px; | |
| font-family: var(--font-code); | |
| font-size: 0.8rem; | |
| } | |
| .btn-download { | |
| background-color: var(--accent); | |
| color: white; | |
| border: none; | |
| padding: 8px; | |
| border-radius: 4px; | |
| cursor: pointer; | |
| font-weight: 600; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 6px; | |
| } | |
| .btn-download:hover { | |
| background-color: var(--accent-hover); | |
| } | |
| /* Terminal Simulation */ | |
| .terminal { | |
| background: #000; | |
| color: #0f0; | |
| font-family: var(--font-code); | |
| font-size: 0.7rem; | |
| padding: 10px; | |
| border-radius: 4px; | |
| height: 150px; | |
| overflow-y: auto; | |
| border: 1px solid #333; | |
| } | |
| .terminal-line { | |
| margin-bottom: 2px; | |
| } | |
| .terminal-line.error { color: var(--danger); } | |
| .terminal-line.info { color: var(--text-muted); } | |
| /* Toast Notification */ | |
| .toast { | |
| position: fixed; | |
| bottom: 20px; | |
| right: 20px; | |
| background: var(--bg-panel); | |
| border: 1px solid var(--accent); | |
| color: var(--text-main); | |
| padding: 12px 20px; | |
| border-radius: 6px; | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.5); | |
| transform: translateY(100px); | |
| opacity: 0; | |
| transition: all 0.3s ease; | |
| z-index: 100; | |
| } | |
| .toast.show { | |
| transform: translateY(0); | |
| opacity: 1; | |
| } | |
| /* Responsive */ | |
| @media (max-width: 900px) { | |
| .right-panel { | |
| display: none; /* Hide right panel on small screens, toggle needed */ | |
| } | |
| } | |
| @media (max-width: 600px) { | |
| .sidebar { | |
| position: absolute; | |
| height: 100%; | |
| transform: translateX(-100%); | |
| } | |
| .sidebar.open { | |
| transform: translateX(0); | |
| } | |
| .header-links { | |
| display: none; | |
| } | |
| } | |
| /* Auto-refresh spinner */ | |
| .spinner { | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { 100% { transform: rotate(360deg); } } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- Header --> | |
| <header> | |
| <div class="logo"> | |
| <i class="fa-solid fa-robot"></i> | |
| <span>Transformers WebApp</span> | |
| </div> | |
| <div class="header-links"> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank">Built with anycoder</a> | |
| </div> | |
| <div> | |
| <button id="toggle-terminal" class="action-btn"><i class="fa-solid fa-terminal"></i> Bash</button> | |
| <button id="toggle-sidebar" class="action-btn" style="display:none;"><i class="fa-solid fa-bars"></i></button> | |
| </div> | |
| </header> | |
| <div class="main-container"> | |
| <!-- Sidebar --> | |
| <aside class="sidebar" id="sidebar"> | |
| <div class="sidebar-header"> | |
| <button class="btn-new-chat" onclick="createNewConversation()"> | |
| <i class="fa-solid fa-plus"></i> New Conversation | |
| </button> | |
| </div> | |
| <div class="history-list" id="history-list"> | |
| <!-- History Items Injected via JS --> | |
| </div> | |
| <div class="sidebar-footer"> | |
| <button class="btn-import" onclick="importConversation()"> | |
| <i class="fa-solid fa-file-import"></i> Import Conv. | |
| </button> | |
| </div> | |
| </aside> | |
| <!-- Main Chat Area --> | |
| <main class="chat-area"> | |
| <div class="chat-header"> | |
| <div class="model-indicator"> | |
| <i class="fa-solid fa-microchip"></i> | |
| <span id="current-model-display">Llama-3-8B</span> | |
| <span class="badge" id="focus-mode-badge">Focus: OFF</span> | |
| </div> | |
| <div> | |
| <button class="action-btn" onclick="toggleFocusMode()" title="Switch to Focus Model"> | |
| <i class="fa-solid fa-crosshairs"></i> Focus | |
| </button> | |
| </div> | |
| </div> | |
| <div class="chat-messages" id="chat-messages"> | |
| <!-- Messages Injected via JS --> | |
| </div> | |
| <div class="input-area"> | |
| <div class="input-wrapper"> | |
| <textarea class="chat-input" id="user-input" placeholder="Type a message... (Shift+Enter for new line)"></textarea> | |
| <button class="send-btn" onclick="sendMessage()"><i class="fa-solid fa-paper-plane"></i></button> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- Right Panel (Models & Tools) --> | |
| <aside class="right-panel" id="right-panel"> | |
| <!-- Model Showcase --> | |
| <div class="panel-section"> | |
| <div class="panel-title">Model Showcase</div> | |
| <div id="model-list"> | |
| <!-- Models injected JS --> | |
| </div> | |
| </div> | |
| <!-- Configuration & Downloads --> | |
| <div class="panel-section"> | |
| <div class="panel-title">Configuration & API</div> | |
| <div class="download-controls"> | |
| <div class="form-group"> | |
| <label>WebApp Folder Path</label> | |
| <input type="text" class="form-input" value=":/app/models" id="app-path"> | |
| </div> | |
| <div class="form-group"> | |
| <label>Download Destination</label> | |
| <input type="text" class="form-input" value="~/Downloads/" id="dl-path"> | |
| </div> | |
| <button class="btn-download" onclick="startDownloadProcess()"> | |
| <i class="fa-solid fa-cloud-arrow-down"></i> Fetch & Download Models | |
| </button> | |
| <button class="btn-download" style="background-color: var(--border); margin-top:5px;" onclick="simulateAutoConfig()"> | |
| <i class="fa-solid fa-wand-magic-sparkles"></i> Auto Config App | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Simulated Terminal --> | |
| <div class="panel-section"> | |
| <div class="panel-title">System Terminal (Bash/Python)</div> | |
| <div class="terminal" id="terminal-output"> | |
| <div class="terminal-line info">> System initialized...</div> | |
| <div class="terminal-line info">> Ready for commands.</div> | |
| </div> | |
| </div> | |
| </aside> | |
| </div> | |
| <!-- Toast Notification --> | |
| <div class="toast" id="toast">Operation Successful</div> | |
| <script> | |
| // --- State Management --- | |
| const state = { | |
| conversations: [], | |
| currentConversationId: null, | |
| models: [ | |
| { id: 'llama-3', name: 'Llama-3-8B', type: 'Instruct', size: '4.7GB', desc: 'General purpose assistant.' }, | |
| { id: 'mistral', name: 'Mistral-7B', type: 'Creative', size: '4.1GB', desc: 'Good for creative writing.' }, | |
| { id: 'falcon', name: 'Falcon-40B', type: 'Logic', size: '80GB', desc: 'Advanced reasoning.' }, | |
| { id: 'bert', name: 'BERT-Large', type: 'NLP', size: '1.2GB', desc: 'Text classification & embeddings.' } | |
| ], | |
| currentModel: 'llama-3', | |
| focusMode: false, | |
| appPath: ':/app/models', | |
| dlPath: '~/Downloads/' | |
| }; | |
| // --- Initialization --- | |
| document.addEventListener('DOMContentLoaded', () => { | |
| loadFromStorage(); | |
| if (state.conversations.length === 0) { | |
| createNewConversation(true); | |
| } else { | |
| loadConversation(state.conversations[0].id); | |
| } | |
| renderModelList(); | |
| renderHistoryList(); | |
| // Auto-refresh simulation (AJAX polling visual) | |
| setInterval(() => { | |
| // Simulate checking for model updates silently | |
| if(Math.random() > 0.95) { | |
| logTerminal(`> Checking HuggingFace API for updates...`, 'info'); | |
| } | |
| }, 10000); | |
| // Textarea auto-resize | |
| const ta = document.getElementById('user-input'); | |
| ta.addEventListener('input', function() { | |
| this.style.height = 'auto'; | |
| this.style.height = (this.scrollHeight) + 'px'; | |
| }); | |
| // Handle Enter key | |
| ta.addEventListener('keydown', function(e) { | |
| if (e.key === 'Enter' && !e.shiftKey) { | |
| e.preventDefault(); | |
| sendMessage(); | |
| } | |
| }); | |
| }); | |
| // --- Core Logic --- | |
| function createNewConversation(isFirst = false) { | |
| const newId = Date.now().toString(); | |
| const newConv = { | |
| id: newId, | |
| name: isFirst ? 'New Conversation' : `Conversation ${state.conversations.length + 1}`, | |
| model: state.currentModel, | |
| messages: [] | |
| }; | |
| // Pre-suggestion based on model | |
| const currentModelObj = state.models.find(m => m.id === state.currentModel); | |
| if (currentModelObj) { | |
| newConv.messages.push({ | |
| role: 'system', | |
| content: `Switched to ${currentModelObj.name}. ${currentModelObj.desc} Suggestion: Try asking for a summary or creative story.` | |
| }); | |
| } | |
| state.conversations.unshift(newConv); | |
| state.currentConversationId = newId; | |
| saveToStorage(); | |
| renderHistoryList(); | |
| renderChat(); | |
| if(!isFirst) showToast("New Conversation Created"); | |
| } | |
| function loadConversation(id) { | |
| state.currentConversationId = id; | |
| const conv = state.conversations.find(c => c.id === id); | |
| if (conv) { | |
| // Update current model to match the conversation's model | |
| if (conv.model && state.models.find(m => m.id === conv.model)) { | |
| state.currentModel = conv.model; | |
| document.getElementById('current-model-display').innerText = state.models.find(m => m.id === conv.model).name; | |
| renderModelList(); // Update active state | |
| } | |
| renderChat(); | |
| renderHistoryList(); // Update active class | |
| } | |
| } | |
| function deleteConversation(id, e) { | |
| e.stopPropagation(); | |
| if(confirm("Delete this conversation?")) { | |
| state.conversations = state.conversations.filter(c => c.id !== id); | |
| if (state.conversations.length === 0) { | |
| createNewConversation(); | |
| } else { | |
| loadConversation(state.conversations[0].id); | |
| } | |
| saveToStorage(); | |
| renderHistoryList(); | |
| } | |
| } | |
| function updateConversationName(id, newName) { | |
| const conv = state.conversations.find(c => c.id === id); | |
| if (conv) { | |
| conv.name = newName; | |
| saveToStorage(); | |
| renderHistoryList(); | |
| } | |
| } | |
| function sendMessage() { | |
| const input = document.getElementById('user-input'); | |
| const text = input.value.trim(); | |
| if (!text) return; | |
| const conv = state.conversations.find(c => c.id === state.currentConversationId); | |
| if (!conv) return; | |
| // Add User Message | |
| conv.messages.push({ role: 'user', content: text }); | |
| input.value = ''; | |
| input.style.height = 'auto'; // Reset height | |
| saveToStorage(); | |
| renderChat(); | |
| // Simulate AI Response | |
| setTimeout(() => { | |
| const aiResponse = generateSimulatedResponse(text); | |
| conv.messages.push({ role: 'ai', content: aiResponse }); | |
| saveToStorage(); | |
| renderChat(); | |
| }, 1000 + Math.random() * 1000); | |
| } | |
| function switchModel(modelId) { | |
| state.currentModel = modelId; | |
| const model = state.models.find(m => m.id === modelId); | |
| // Update UI | |
| document.getElementById('current-model-display').innerText = model.name; | |
| renderModelList(); | |
| // Add intro suggestion to current chat | |
| const conv = state.conversations.find(c => c.id === state.currentConversationId); | |
| if (conv) { | |
| conv.model = modelId; | |
| conv.messages.push({ | |
| role: 'system', | |
| content: `Model switched to ${model.name}. Intro suggestion: This model is optimized for ${model.type} tasks.` | |
| }); | |
| saveToStorage(); | |
| renderChat(); | |
| } | |
| showToast(`Switched to ${model.name}`); | |
| } | |
| function toggleFocusMode() { | |
| state.focusMode = !state.focusMode; | |
| const badge = document.getElementById('focus-mode-badge'); | |
| if (state.focusMode) { | |
| badge.style.background = 'var(--accent)'; | |
| badge.style.color = '#fff'; | |
| badge.innerText = 'Focus: ON'; | |
| showToast("Focus Mode Enabled: Distractions hidden"); | |
| } else { | |
| badge.style.background = 'var(--border)'; | |
| badge.style.color = 'var(--text-muted)'; | |
| badge.innerText = 'Focus: OFF'; | |
| showToast("Focus Mode Disabled"); | |
| } | |
| } | |
| function importConversation() { | |
| // Simulate file reading | |
| const input = document.createElement('input'); | |
| input.type = 'file'; | |
| input.accept = '.json'; | |
| input.onchange = e => { | |
| const file = e.target.files[0]; | |
| if (file) { | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| try { | |
| const imported = JSON.parse(e.target.result); | |
| imported.id = Date.now().toString(); // Ensure unique ID | |
| state.conversations.unshift(imported); | |
| saveToStorage(); | |
| renderHistoryList(); | |
| loadConversation(imported.id); | |
| showToast("Conversation Imported Successfully"); | |
| logTerminal(`> Imported ${file.name} to repository.`, 'info'); | |
| } catch (err) { | |
| showToast("Invalid JSON file"); | |
| logTerminal(`> Error importing file: ${err.message}`, 'error'); | |
| } | |
| }; | |
| reader.readAsText(file); | |
| } | |
| }; | |
| input.click(); | |
| } | |
| // --- Render Functions --- | |
| function renderHistoryList() { | |
| const list = document.getElementById('history-list'); | |
| list.innerHTML = ''; | |
| state.conversations.forEach(conv => { | |
| const div = document.createElement('div'); | |
| div.className = `history-item ${conv.id === state.currentConversationId ? 'active' : ''}`; | |
| div.onclick = () => loadConversation(conv.id); | |
| div.innerHTML = ` | |
| <div class="history-name" contenteditable="true" onblur="updateConversationName('${conv.id}', this.innerText)">${conv.name}</div> | |
| <div class="history-actions"> | |
| <i class="fa-solid fa-trash" onclick="deleteConversation('${conv.id}', event)"></i> | |
| </div> | |
| `; | |
| list.appendChild(div); | |
| }); | |
| } | |
| function renderChat() { | |
| const container = document.getElementById('chat-messages'); | |
| const conv = state.conversations.find(c => c.id === state.currentConversationId); | |
| container.innerHTML = ''; | |
| if (!conv) return; | |
| conv.messages.forEach(msg => { | |
| const div = document.createElement('div'); | |
| div.className = `message ${msg.role}`; | |
| div.innerHTML = ` | |
| <div class="msg-actions"> | |
| <button class="action-btn" onclick="copyText(this)" title="Copy"> | |
| <i class="fa-regular fa-copy"></i> | |
| </button> | |
| <button class="action-btn" onclick="pasteText(this)" title="Paste (Simulated)"> | |
| <i class="fa-solid fa-paste"></i> | |
| </button> | |
| </div> | |
| <div class="bubble">${escapeHtml(msg.content)}</div> | |
| `; | |
| container.appendChild(div); | |
| }); | |
| // Scroll to bottom | |
| container.scrollTop = container.scrollHeight; | |
| } | |
| function renderModelList() { | |
| const list = document.getElementById('model-list'); | |
| list.innerHTML = ''; | |
| state.models.forEach(model => { | |
| const div = document.createElement('div'); | |
| div.className = `model-card ${model.id === state.currentModel ? 'active' : ''}`; | |
| div.onclick = () => switchModel(model.id); | |
| div.innerHTML = ` | |
| <div class="model-header"> | |
| <span class="model-name">${model.name}</span> | |
| <i class="fa-solid fa-check" style="opacity: ${model.id === state.currentModel ? 1 : 0}; color: var(--accent);"></i> | |
| </div> | |
| <div class="model-meta">${model.desc}</div> | |
| <div class="showcase-stats"> | |
| <span class="stat">${model.type}</span> | |
| <span class="stat">${model.size}</span> | |
| </div> | |
| `; | |
| list.appendChild(div); | |
| }); | |
| } | |
| // --- Features & Simulation --- | |
| function copyText(btn) { | |
| const text = btn.closest('.message').querySelector('.bubble').innerText; | |
| navigator.clipboard.writeText(text).then(() => { | |
| showToast("Copied to clipboard"); | |
| }); | |
| } | |
| function pasteText(btn) { | |
| // Browser security prevents real paste without user gesture on active element | |
| // We simulate reading from clipboard for the UI demo | |
| navigator.clipboard.readText().then(text => { | |
| const input = document.getElementById('user-input'); | |
| input.value += text; | |
| input.focus(); | |
| showToast("Pasted from clipboard"); | |
| }).catch(() => { | |
| showToast("Clipboard permission denied"); | |
| }); | |
| } | |
| function startDownloadProcess() { | |
| const appPath = document.getElementById('app-path').value; | |
| const dlPath = document.getElementById('dl-path').value; | |
| logTerminal(`> Initializing HuggingFace API client...`, 'info'); | |
| logTerminal(`> Fetching model list...`, 'info'); | |
| setTimeout(() => { | |
| logTerminal(`> Found 4 new models available.`, 'info'); | |
| logTerminal(`> Starting download to ${dlPath}...`, 'info'); | |
| simulateProgress(); | |
| }, 1000); | |
| } | |
| function simulateProgress() { | |
| let progress = 0; | |
| const interval = setInterval(() => { | |
| progress += 10; | |
| if (progress <= 100) { | |
| logTerminal(`> Downloading model weights... ${progress}%`); | |
| } else { | |
| clearInterval(interval); | |
| completeDownload(); | |
| } | |
| }, 300); | |
| } | |
| function completeDownload() { | |
| const appPath = document.getElementById('app-path').value; | |
| logTerminal(`> Download complete. Verifying checksums...`, 'info'); | |
| logTerminal(`> Moving files to ${appPath}...`, 'info'); | |
| logTerminal(`> python bash_script.py --move-source ~/Downloads/ --dest ${appPath}`, 'info'); | |
| setTimeout(() => { | |
| logTerminal(`> SUCCESS: Models integrated into app folder.`, 'info'); | |
| showToast("Models Downloaded & Integrated"); | |
| }, 1000); | |
| } | |
| function simulateAutoConfig() { | |
| logTerminal(`> Auto-configuring application...`); | |
| logTerminal(`> Detecting system architecture... x86_64`); | |
| logTerminal(`> Setting default paths... Done.`); | |
| logTerminal(`> Optimizing CSS3 rendering... Done.`); | |
| setTimeout(() => { | |
| showToast("Auto Configuration Complete"); | |
| }, 800); | |
| } | |
| function logTerminal(msg, type = '') { | |
| const term = document.getElementById('terminal-output'); | |
| const line = document.createElement('div'); | |
| line.className = `terminal-line ${type}`; | |
| line.innerText = msg; | |
| term.appendChild(line); | |
| term.scrollTop = term.scrollHeight; | |
| } | |
| // --- Utilities --- | |
| function showToast(message) { | |
| const toast = document.getElementById('toast'); | |
| toast.innerText = message; | |
| toast.classList.add('show'); | |
| setTimeout(() => { | |
| toast.classList.remove('show'); | |
| }, 3000); | |
| } | |
| function escapeHtml(text) { | |
| const map = { | |
| '&': '&', | |
| '<': '<', | |
| '>': '>', | |
| '"': '"', | |
| "'": ''' | |
| }; | |
| return text.replace(/[&<>"']/g, function(m) { return map[m]; }); | |
| } | |
| function generateSimulatedResponse(input) { | |
| const responses = [ | |
| "That is an interesting perspective. Could you elaborate on how this relates to the model architecture?", | |
| "I have processed your request using the current transformer model. The output suggests a high probability of success.", | |
| "Based on the file repository analysis, the configuration seems optimal.", | |
| "Here is a suggestion for your code: Ensure you are handling the asynchronous nature of the AJAX calls correctly.", | |
| "I can help you move those files. Please confirm the destination path in the settings panel." | |
| ]; | |
| return responses[Math.floor(Math.random() * responses.length)]; | |
| } | |
| // --- Storage --- | |
| function saveToStorage() { | |
| localStorage.setItem('transformers_app_data', JSON.stringify(state.conversations)); | |
| } | |
| function loadFromStorage() { | |
| const data = localStorage.getItem('transformers_app_data'); | |
| if (data) { | |
| state.conversations = JSON.parse(data); | |
| } | |
| } | |
| // Mobile Sidebar Toggle | |
| document.getElementById('toggle-sidebar').addEventListener('click', () => { | |
| document.getElementById('sidebar').classList.toggle('open'); | |
| }); | |
| // Terminal Toggle | |
| document.getElementById('toggle-terminal').addEventListener('click', () => { | |
| const panel = document.getElementById('right-panel'); | |
| if(panel.style.display === 'none') { | |
| panel.style.display = 'flex'; | |
| } else { | |
| panel.style.display = 'none'; | |
| } | |
| }); | |
| </script> | |
| </body> | |
| </html> |