Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>AI Chat Canvas - Powered by Transformers.js</title> | |
| <link rel="stylesheet" href="style.css"> | |
| </head> | |
| <body> | |
| <div class="app-container"> | |
| <!-- Header --> | |
| <header class="app-header"> | |
| <div class="header-content"> | |
| <div class="logo-area"> | |
| <svg class="logo-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <path d="M12 2a10 10 0 1 0 10 10A10 10 0 0 0 12 2zm0 18a8 8 0 1 1 8-8 8 8 0 0 1-8 8z"/> | |
| <path d="M12 6v6l4 2"/> | |
| </svg> | |
| <h1>AI Chat Canvas</h1> | |
| </div> | |
| <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="attribution-link"> | |
| Built with anycoder | |
| </a> | |
| </div> | |
| <!-- Model Status --> | |
| <div class="model-status" id="model-status"> | |
| <span class="status-dot"></span> | |
| <span class="status-text">Initializing...</span> | |
| </div> | |
| </header> | |
| <!-- Main Content --> | |
| <main class="main-content"> | |
| <!-- Chat Messages --> | |
| <div class="chat-container" id="chat-container"> | |
| <div class="welcome-screen" id="welcome-screen"> | |
| <div class="welcome-icon">🤖</div> | |
| <h2>Welcome to AI Chat Canvas</h2> | |
| <p>This is a browser-based chat application running entirely on your device using transformers.js.</p> | |
| <div class="example-prompts"> | |
| <p class="example-label">Try asking:</p> | |
| <button class="example-btn" data-prompt="Explain quantum computing in simple terms">What is quantum computing?</button> | |
| <button class="example-btn" data-prompt="Write a Python function to sort a list">Help me write Python code</button> | |
| <button class="example-btn" data-prompt="What are the benefits of remote work?">Discuss remote work benefits</button> | |
| </div> | |
| </div> | |
| <div class="messages-list" id="messages-list"></div> | |
| <!-- Loading Indicator --> | |
| <div class="loading-indicator hidden" id="loading-indicator"> | |
| <div class="spinner"></div> | |
| <div class="loading-text"> | |
| <span class="loading-dot"></span> | |
| <span class="loading-dot"></span> | |
| <span class="loading-dot"></span> | |
| </div> | |
| <p id="loading-message">Loading model...</p> | |
| <div class="progress-bar" id="progress-bar"> | |
| <div class="progress-fill" id="progress-fill"></div> | |
| </div> | |
| </div> | |
| </div> | |
| </main> | |
| <!-- Input Area --> | |
| <footer class="input-area"> | |
| <div class="input-wrapper"> | |
| <textarea | |
| id="user-input" | |
| placeholder="Type your message here... (Shift+Enter for new line)" | |
| rows="1" | |
| ></textarea> | |
| <button id="send-btn" class="send-btn" disabled> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"> | |
| <line x1="22" y1="2" x2="11" y2="13"></line> | |
| <polygon points="22 2 15 22 11 13 2 9 22 2"></polygon> | |
| </svg> | |
| </button> | |
| </div> | |
| <div class="disclaimer"> | |
| <p>AI responses may be inaccurate. Please verify important information.</p> | |
| </div> | |
| </footer> | |
| </div> | |
| <script type="module"> | |
| // ============================================ | |
| // WORKER CODE (Inline for single-file deployment) | |
| // ============================================ | |
| const workerScript = ` | |
| import { pipeline, env } from 'https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.8.0'; | |
| // Configure environment | |
| env.allowLocalModels = false; | |
| env.useBrowserCache = true; | |
| let generator = null; | |
| let currentAbortController = null; | |
| self.onmessage = async (e) => { | |
| const { type, payload } = e.data; | |
| if (type === 'load') { | |
| currentAbortController = new AbortController(); | |
| try { | |
| self.postMessage({ | |
| type: 'loading', | |
| progress: 0, | |
| message: 'Initializing model...' | |
| }); | |
| // Load text generation pipeline | |
| generator = await pipeline('text-generation', payload.model, { | |
| progress_callback: (data) => { | |
| if (data.status === 'progress') { | |
| self.postMessage({ | |
| type: 'progress', | |
| progress: data.progress, | |
| message: data.file ? \`Downloading: \${data.file}\` : 'Loading model...' | |
| }); | |
| } else if (data.status === 'progress-queue') { | |
| self.postMessage({ | |
| type: 'progress', | |
| progress: 0, | |
| message: \`Queued: \${data.file}\` | |
| }); | |
| } | |
| }, | |
| signal: currentAbortController.signal | |
| }); | |
| self.postMessage({ type: 'loaded' }); | |
| } catch (error) { | |
| if (error.name === 'AbortError') { | |
| self.postMessage({ type: 'cancelled' }); | |
| } else { | |
| self.postMessage({ type: 'error', error: error.message }); | |
| } | |
| } | |
| } | |
| if (type === 'generate') { | |
| currentAbortController = new AbortController(); | |
| try { | |
| // Clear previous abort controller | |
| if (generator) { | |
| const result = await generator(payload.prompt, { | |
| max_new_tokens: payload.maxTokens || 512, | |
| temperature: payload.temperature || 0.7, | |
| top_p: payload.topP || 0.9, | |
| do_sample: payload.doSample !== false, | |
| return_full_text: false, | |
| repetition_penalty: payload.repetitionPenalty || 1.1, | |
| signal: currentAbortController.signal | |
| }); | |
| self.postMessage({ | |
| type: 'result', | |
| result: result[0].generated_text | |
| }); | |
| } | |
| } catch (error) { | |
| if (error.name === 'AbortError') { | |
| self.postMessage({ type: 'cancelled' }); | |
| } else { | |
| self.postMessage({ type: 'error', error: error.message }); | |
| } | |
| } | |
| } | |
| if (type === 'stop') { | |
| if (currentAbortController) { | |
| currentAbortController.abort(); | |
| } | |
| } | |
| }; | |
| `; | |
| // Create worker from inline script | |
| const blob = new Blob([workerScript], { type: 'application/javascript' }); | |
| const worker = new Worker(URL.createObjectURL(blob)); | |
| </script> | |
| <script type="module" src="index.js"></script> | |
| </body> | |
| </html> |