### Android Install Script Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/README.md Command to install the AI engine on Android via Termux. ```bash bash Android/install.sh ``` -------------------------------- ### Local Fonts Definition Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Defines local font faces for 'Inter' and 'JetBrains Mono' using WOFF2 format, intended to be installed by a platform installer. ```css @font-face { font-family: 'Inter'; src: url('./vendor/Inter-Regular.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; } @font-face { font-family: 'Inter'; src: url('./vendor/Inter-Medium.woff2') format('woff2'); font-weight: 500; font-style: normal; font-display: swap; } @font-face { font-family: 'Inter'; src: url('./vendor/Inter-SemiBold.woff2') format('woff2'); font-weight: 600; font-style: normal; font-display: swap; } @font-face { font-family: 'Inter'; src: url('./vendor/Inter-Bold.woff2') format('woff2'); font-weight: 700; font-style: normal; font-display: swap; } @font-face { font-family: 'JetBrains Mono'; src: url('./vendor/JetBrainsMono-Regular.woff2') format('woff2'); font-weight: 400; font-style: normal; font-display: swap; } @font-face { font-family: 'JetBrains Mono'; src: url('./vendor/JetBrainsMono-Medium.woff2') format('woff2'); font-weight: 500; font-style: normal; font-display: swap; } ``` -------------------------------- ### Initialization Function Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html The main initialization function that sets up the UI, loads data, and binds event listeners. ```javascript async function init() { setupMarked(); applyTheme(S.theme); if (!S.sbOpen) D.sb.classList.add('off'); await loadGlobalPrompt(); await fetchModels(); await load(); renderSB(); renderChat(); bind(); if (S.convs.length > 0) switchConv(S.convs[0].id); pollHW(); setInterval(pollHW, 5000); setInterval(fetchModels, 15000); } ``` -------------------------------- ### Event Listeners and Initialization Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Sets up event listeners for various UI elements like theme toggling, clearing messages, file attachments, and predefined prompts. It also includes the initialization function call. ```javascript k', toggleTheme); D.thSb.addEventListener('click', toggleTheme); D.ca.addEventListener('click', () => { if (!S.streaming) clearAll(); }); D.att.addEventListener('click', () => D.fInp.click()); D.fInp.addEventListener('change', async (e) => { await handleAttach(e.target.files); e.target.value = ''; }); $$('.sc').forEach((c) => c.addEventListener('click', () => { const p = c.dataset.prompt; if (p) sendMsg(p); }), ); // Panel toggles D.modelBtn.addEventListener('click', (e) => { e.stopPropagation(); toggleModelMenu(); D.sysPanel.classList.remove('open'); }); D.sysBtn.addEventListener('click', (e) => { e.stopPropagation(); D.sysPanel.classList.toggle('open'); toggleModelMenu(false); }); D.logModeSel.addEventListener('change', saveLogMode); D.sysSet.addEventListener('click', saveGlobalPrompt); D.sysClr.addEventListener('click', clearGlobalPrompt); document.addEventListener('click', (e) => { if (!D.modelDd.contains(e.target)) toggleModelMenu(false); if (!D.sysPanel.contains(e.target) && !D.sysBtn.contains(e.target)) D.sysPanel.classList.remove('open'); }); window.addEventListener('resize', () => { if (window.innerWidth <= 768 && S.sbOpen) toggleSB(false); }); // Drag-and-drop (image / PDF / text files) const mainEl = document.getElementById('main'); mainEl.addEventListener('dragover', (e) => { e.preventDefault(); mainEl.classList.add('drag-over'); }); mainEl.addEventListener('dragleave', () => mainEl.classList.remove('drag-over'), ); mainEl.addEventListener('drop', (e) => { e.preventDefault(); mainEl.classList.remove('drag-over'); handleAttach(e.dataTransfer.files); }); init(); ``` -------------------------------- ### Get current conversation Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Retrieves the current conversation object from the state based on the current conversation ID. ```javascript function getConv() { return S.convs.find((c) => c.id === S.curId); } ``` -------------------------------- ### Load Global Prompt Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Loads the global system prompt and log mode from either server settings or local storage. ```javascript async function loadGlobalPrompt() { if (IS_SERVED) { try { const r = await fetch('/api/settings'); if (r.ok) { const s = await r.json(); S.globalSys = s.globalSystemPrompt || ''; S.logMode = s.logMode === 'all' ? 'all' : 'errors_only'; } } catch {} } else { S.globalSys = localStorage.getItem('globalSystemPrompt') || ''; S.logMode = localStorage.getItem('logMode') || 'errors_only'; } updateSysUI(); } ``` -------------------------------- ### Markdown Rendering with Code Highlighting Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Configures the 'marked' library to render markdown, specifically customizing the code block rendering to include syntax highlighting using highlight.js and adding a copy button. ```javascript function setupMarked() { if (typeof marked === 'undefined') return; const rdr = new marked.Renderer(); rdr.code = (code, lang) => { const sl = esc(lang || ''); const dl = sl || 'code'; let hi = esc(code); if (typeof hljs !== 'undefined') { try { hi = lang && hljs.getLanguage(lang) ? hljs.highlight(code, { language: lang }).value : hljs.highlightAuto(code).value; } catch {} } return `
${dl}
${hi}
`; }; marked.setOptions({ gfm: true, breaks: true, renderer: rdr }); } function renderMd(text) { if (typeof marked === 'undefined') return esc(text).replace(/\n/g, '
'); return marked.parse(text, { breaks: true }); } ``` -------------------------------- ### Layout Shell Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Defines the main application layout using flexbox to create a full-viewport container with a sidebar. ```css #app { display: flex; height: 100vh; width: 100vw; overflow: hidden; } ``` -------------------------------- ### Configuration Constants Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Defines constants for server serving status, Ollama endpoint, vision models, and application state. ```javascript const IS_SERVED = location.protocol === 'http:' || location.protocol === 'https:'; const OLLAMA = IS_SERVED ? '/ollama' : 'http://127.0.0.1:11434'; const VISION_MODELS = [ 'llava', 'moondream', 'bakllava', 'vision', 'minicpm-v', 'cogvlm', 'qwen-vl', 'phi-3-vision', ]; const S = { convs: [], curId: null, theme: localStorage.getItem('g-theme') || 'dark', sbOpen: window.innerWidth > 768, streaming: false, abort: null, models: [], model: localStorage.getItem('g-model') || '', globalSys: '', logMode: localStorage.getItem('logMode') || 'errors_only', // Attachments (multi-file) attachments: [], }; ``` -------------------------------- ### Responsive Design Media Queries Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html CSS media queries for adjusting the layout on smaller screens (max-width: 768px) and for reduced motion. ```css /* RESPONSIVE */ @media (max-width: 768px) { #sidebar { position: fixed; left: 0; top: 0; height: 100%; } #sidebar.off { transform: translateX(-100%); } .sg { grid-template-columns: 1fr; } .usr .mc { max-width: 88%; } .wt { font-size: 30px; } #inp-area { padding: 6px 12px 14px; } #msgs { padding: 16px 16px 12px; } } @media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; } } ``` -------------------------------- ### Model Rendering Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Renders the list of available models in the UI, highlighting the currently selected model and displaying its size. It also attaches click event listeners to each model option for selection. ```javascript function renderModelMenu() { if (!S.models.length) return; D.modelMenu.innerHTML = S.models .map( (m) => `
${esc(m.name)}
${(m.size / 1e9).toFixed(1)} GB
`, ) .join(''); $$('.mm-opt').forEach((opt) => opt.addEventListener('click', (e) => { e.stopPropagation(); const model = opt.dataset.model; applyModel(model); const conv = getConv(); if (conv) { conv.model = model; save(); } toggleModelMenu(false); })); } ``` -------------------------------- ### Ensure PDF.js is loaded Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Asynchronously loads the PDF.js library if it's not already available, setting up the worker source. It also handles potential loading failures. ```javascript async function ensurePdfJs() { if (window.pdfjsLib) return true; if (pdfJsLoading) return new Promise((res) => { const iv = setInterval(() => { if (window.pdfjsLib || window._pdfFailed) { clearInterval(iv); res(!!window.pdfjsLib); } }, 100); }); pdfJsLoading = true; try { const m = await import('./vendor/pdf.min.mjs'); window.pdfjsLib = m; window.pdfjsLib.GlobalWorkerOptions.workerSrc = './vendor/pdf.worker.min.mjs'; return true; } catch { window._pdfFailed = true; return false; } } ``` -------------------------------- ### Theme Management Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Functions to apply and toggle between light and dark themes. ```javascript function applyTheme(t) { S.theme = t; document.documentElement.setAttribute('data-theme', t); localStorage.setItem('g-theme', t); const ic = t === 'dark' ? 'fa-moon' : 'fa-sun'; D.thTop.querySelector('i').className = 'fa-solid ' + ic; D.thSb.querySelector('i').className = 'fa-solid ' + ic; } function toggleTheme() { applyTheme(S.theme === 'dark' ? 'light' : 'dark'); } ``` -------------------------------- ### File/Image Preview Bar Styles Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html CSS for displaying previews of uploaded files or images. ```css /* File/image preview bar */ .fbar { max-width: 740px; width: 100%; display: none; gap: 10px; flex-wrap: wrap; } .fbar.on { display: flex; padding: 10px 14px 4px; } .img-preview { position: relative; width: 60px; height: 60px; border-radius: 10px; overflow: hidden; border: 1px solid var(--bd); } .img-preview img { width: 100%; height: 100%; object-fit: cover; } .pdf-preview { display: flex; align-items: center; gap: 10px; padding: 8px 14px; background: var(--bg3); border-radius: 12px; border: 1px solid var(--bd); } .pdf-preview .p-info { display: flex; flex-direction: column; max-width: 130px; } .pdf-preview strong { font-size: 12px; color: var(--t1); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .pdf-preview span { font-size: 10px; color: var(--t3); } .f-rm { position: absolute; top: 2px; right: 2px; background: rgba(0, 0, 0, 0.65); color: #fff; border: none; border-radius: 50%; width: 20px; height: 20px; font-size: 10px; cursor: pointer; display: flex; align-items: center; justify-content: center; } .pdf-preview .f-rm { position: relative; top: 0; right: 0; background: transparent; color: var(--t3); } .pdf-preview .f-rm:hover { color: var(--red); } ``` -------------------------------- ### Clear Global Prompt Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Clears the global system prompt and updates the UI and storage. ```javascript async function clearGlobalPrompt() { S.globalSys = ''; D.sysTa.value = ''; S.logMode = D.logModeSel.value === 'all' ? 'all' : 'errors_only'; if (IS_SERVED) { try { await fetch('/api/settings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ globalSystemPrompt: '', logMode: S.logMode, }), }); } catch {} } else { localStorage.removeItem('globalSystemPrompt'); localStorage.setItem('logMode', S.logMode); } updateSysUI(); toast('Default prompt cleared'); } ``` -------------------------------- ### Create a new conversation Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Initializes a new chat conversation with a default title, empty messages, and the current model and system prompt settings. It then switches to this new conversation and renders the UI. ```javascript function createConv() { const c = { id: gid(), title: 'New chat', msgs: [], ts: Date.now(), model: S.model, sys: S.globalSys, }; S.convs.unshift(c); save(); switchConv(c.id); renderSB(); D.inp.focus(); } ``` -------------------------------- ### Code Styling Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html CSS for inline code and code blocks, including syntax highlighting and copy buttons. ```css .mt code:not(.hljs) { background: var(--bg3); padding: 2px 6px; border-radius: 6px; font-family: 'JetBrains Mono', 'SF Mono', monospace; font-size: 0.88em; border: 1px solid var(--bd); } .cblk { margin: 16px 0; border-radius: 12px; overflow: hidden; border: 1px solid var(--bd); background: var(--code); } .cbh { display: flex; align-items: center; justify-content: space-between; padding: 9px 14px; background: var(--bg2); border-bottom: 1px solid var(--bd); font-size: 12px; color: var(--t3); } .cbc-btn { background: none; border: none; color: var(--t3); cursor: pointer; font-size: 12px; padding: 3px 8px; border-radius: 6px; transition: background 0.15s, color 0.15s; display: flex; align-items: center; gap: 5px; font-family: var(--font); } .cbc-btn:hover { background: var(--bgh); color: var(--t1); } .cblk pre { padding: 16px; overflow-x: auto; margin: 0; background: transparent !important; } .cblk pre code { font-family: 'JetBrains Mono', 'SF Mono', monospace; font-size: 13px; line-height: 1.65; background: transparent !important; padding: 0; } ``` -------------------------------- ### Input Wrapper Styles Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html CSS for the main input wrapper, providing a rounded rectangle appearance and focus states. ```css /* The main input wrapper – pill/rounded-rectangle */ .iw { max-width: 740px; width: 100%; display: flex; flex-direction: column; background: var(--bg-inp); border: 1px solid var(--bd); border-radius: 22px; transition: border-color 0.2s, box-shadow 0.2s; overflow: visible; position: relative; } .iw:focus-within, .iw.ht { border-color: var(--bd2); box-shadow: 0 2px 20px var(--shd); } ``` -------------------------------- ### Android Launch Script Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/README.md Command to launch the AI engine on Android via Termux. ```bash bash Android/start.sh ``` -------------------------------- ### Launch Scripts Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/README.md Scripts to launch the AI engine on different operating systems. ```bash Windows/start-fast-chat.bat ``` ```bash Mac/start.command ``` ```bash Linux/start.sh ``` ```bash Android/start.sh ``` -------------------------------- ### Handle PDF file upload Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Processes an uploaded PDF file by loading it using PDF.js, extracting text content from each page, and adding it as an attachment to the current conversation state. ```javascript async function handlePdf(file) { const ok = await ensurePdfJs(); if (!ok) return false; try { const buf = await file.arrayBuffer(); const pdf = await window.pdfjsLib.getDocument({ data: buf }).promise; let text = ''; for (let i = 1; i <= pdf.numPages; i++) { const pg = await pdf.getPage(i); const c = await pg.getTextContent(); text += `--- Page ${i} ---\n` + c.items.map((x) => x.str).join(' ') + '\n\n'; } S.attachments.push({ id: gid(), type: 'doc', kind: 'pdf', name: file.name, text: text.trim(), pages: pdf.numPages, }); return true; } catch (e) { return false; } } ``` -------------------------------- ### Model Dropdown Menu Styles Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html CSS for the dropdown menu that appears when selecting a model. ```css /* Model dropdown menu */ .model-menu { position: absolute; bottom: calc(100% + 8px); left: 0; background: var(--bg3); border: 1px solid var(--bd); border-radius: 16px; padding: 6px; min-width: 250px; max-height: 320px; overflow-y: auto; z-index: 200; box-shadow: 0 12px 36px var(--shd); display: none; animation: menuPop 0.15s ease; } .model-menu.on { display: block; } @keyframes menuPop { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } } .mm-opt { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 10px; cursor: pointer; transition: background 0.13s; } .mm-opt:hover { background: var(--bgh); } .mm-opt.sel { background: var(--bga); } .mmo-icon { width: 28px; height: 28px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 12px; flex-shrink: 0; background: linear-gradient( 135deg, var(--grad1), var(--grad2), var(--grad3) ); color: #fff; } .mmo-info { flex: 1; min-width: 0; } .mmo-name { font-size: 13px; font-weight: 500; color: var(--t1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .mmo-desc { font-size: 11px; color: var(--t3); margin-top: 1px; } .mmo-chk { color: var(--grad1); font-size: 12px; opacity: 0; } .mm-opt.sel .mmo-chk { opacity: 1; } ``` -------------------------------- ### Save Global Prompt Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Saves the current global system prompt and log mode to server settings or local storage. ```javascript async function saveGlobalPrompt() { S.globalSys = D.sysTa.value.trim(); S.logMode = D.logModeSel.value === 'all' ? 'all' : 'errors_only'; if (IS_SERVED) { try { await fetch('/api/settings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ globalSystemPrompt: S.globalSys, logMode: S.logMode, }), }); } catch {} } else { localStorage.setItem('globalSystemPrompt', S.globalSys); localStorage.setItem('logMode', S.logMode); } updateSysUI(); toast('Default prompt saved'); } ``` -------------------------------- ### Reset and Base Styles Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Applies a universal reset for margin, padding, and box-sizing, and sets base styles for html and body including height, overflow, font, background, color, and transitions. ```css *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; } html, body { height: 100%; overflow: hidden; font-family: var(--font); background: var(--bg); color: var(--t1); font-size: 15px; line-height: 1.6; transition: background var(--spd), color var(--spd); -webkit-font-smoothing: antialiased; } ``` -------------------------------- ### Handle Text file upload Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Processes an uploaded text file by reading its content and adding it as a document attachment to the current conversation state. ```javascript async function handleText(file) { const text = await file.text(); S.attachments.push({ id: gid(), type: 'doc', kind: 'text', name: file.name, text, pages: 0, }); return true; } ``` -------------------------------- ### Temperature Control CSS Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html CSS styles for the temperature control input and label. ```css /* temperature control */ .temp-ctrl { margin-left: auto; display: flex; align-items: center; gap: 5px; } .temp-lbl { font-size: 11px; color: var(--t3); font-weight: 500; } #temp-input { background: var(--bg3); border: 1px solid var(--bd); color: var(--t1); font-size: 12px; padding: 3px 6px; border-radius: 6px; width: 54px; text-align: center; font-family: var(--font); } #temp-input:focus { outline: none; border-color: var(--bd2); } ``` -------------------------------- ### Attachment and Action Styling Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html CSS for styling image attachments, PDF pills, and message action buttons (copy, like, dislike). ```css .msg-img { max-width: 280px; border-radius: 12px; display: block; border: 1px solid var(--bd); } .msg-pdf-pill { display: inline-flex; align-items: center; gap: 6px; background: var(--bg3); padding: 6px 12px; border-radius: 16px; font-size: 12px; color: var(--t2); border: 1px solid var(--bd); } .usr-attach { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; margin-top: 6px; } .mact { display: flex; gap: 1px; margin-top: 8px; opacity: 0; transition: opacity 0.18s; } .mr:hover .mact { opacity: 1; } .mab { background: none; border: none; color: var(--t3); cursor: pointer; padding: 6px 8px; border-radius: 8px; font-size: 13px; transition: background 0.15s, color 0.15s; display: flex; align-items: center; gap: 4px; } .mab:hover { background: var(--bgh); color: var(--t1); } ``` -------------------------------- ### Event Bindings Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Attaches event listeners to UI elements for user interactions. ```javascript function bind() { D.send.addEventListener('click', () => S.streaming ? stopStream() : sendMsg(D.inp.value), ); D.inp.addEventListener('input', () => { autoResize(); updateSend(); }); D.inp.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); if (!S.streaming) sendMsg(D.inp.value); } if (e.key === 'Escape' && S.streaming) stopStream(); }); D.nc.addEventListener('click', () => { if (!S.streaming) createConv(); }); D.sbTog.addEventListener('click', () => toggleSB()); D.ov.addEventListener('click', () => toggleSB(false)); D.thTop.addEventListener('clic' ) } ``` -------------------------------- ### Markdown Styling Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html CSS rules for rendering markdown elements like paragraphs, headings, lists, tables, and links. ```css .mt p { margin-bottom: 12px; } .mt p:last-child { margin-bottom: 0; } .mt h1, .mt h2, .mt h3 { font-weight: 600; margin: 20px 0 10px; color: var(--t1); } .mt h1 { font-size: 1.35em; } .mt h2 { font-size: 1.18em; } .mt h3 { font-size: 1.06em; } .mt strong { font-weight: 600; } .mt a { color: var(--grad1); text-decoration: none; } .mt a:hover { text-decoration: underline; } .mt ul, .mt ol { margin: 12px 0; padding-left: 24px; } .mt li { margin-bottom: 5px; } .mt hr { border: none; border-top: 1px solid var(--bd); margin: 20px 0; } .mt table { width: 100%; border-collapse: collapse; margin: 16px 0; font-size: 14px; border-radius: 10px; overflow: hidden; border: 1px solid var(--bd); } .mt th, .mt td { padding: 8px 14px; border: 1px solid var(--bd); text-align: left; } .mt th { background: var(--bg2); font-weight: 600; } ``` -------------------------------- ### Set Hardware Bar Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Updates the visual representation and text content of hardware usage bars. ```javascript function setBar(type, pct) { const bar = $(\`#${type}-bar\]`); const lbl = $(\`#${type}-pct\]`); if (!bar) return; bar.style.transform = \`scaleX(${Math.max(0, Math.min(100, pct)) / 100})\]`; lbl.textContent = pct + '%'; lbl.className = 'hw-pct' + (pct >= 90 ? ' danger' : pct >= 70 ? ' warn' : ''); } ``` -------------------------------- ### Attachment Handling Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Handles the process of attaching files (images, PDFs, text files) to the current context. It validates file types and initiates specific handlers for each type. ```javascript async function handleAttach(files) { const list = Array.from(files || []).filter(Boolean); if (!list.length) return; let added = 0; let rejected = 0; for (const file of list) { const ok = await handleSingleAttachment(file); if (ok) added++; else rejected++; } if (added) { renderAttachmentBar(); checkVisionWarn(); updateSend(); } if (rejected) { toast( rejected === list.length ? 'No supported files selected' : `${rejected} file(s) skipped`, ); } } ``` -------------------------------- ### DOM Element References Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Provides utility functions and references to frequently used DOM elements for UI manipulation. ```javascript const $ = (s) => document.querySelector(s); const $$ = (s) => document.querySelectorAll(s); const D = { sb: $('#sidebar'), ov: $('#overlay'), cvList: $('#cv-list'), welcome: $('#welcome'), msgs: $('#msgs'), chat: $('#chat'), inp: $('#msg-inp'), iw: $('#iw'), send: $('#send-btn'), att: $('#att-btn'), fInp: $('#f-inp'), fBar: $('#fbar'), nc: $('#nc-btn'), sbTog: $('#sb-tog'), thTop: $('#th-top'), thSb: $('#th-sb'), ca: $('#ca-btn'), toasts: $('#toasts'), modelBtn: $('#model-btn'), modelMenu: $('#model-menu'), modelName: $('#model-name'), tbTitle: $('#tb-title'), modelDd: $('#model-dd'), // Extensions sysBtn: $('#sys-prompt-btn'), sysPanel: $('#sys-panel'), sysTa: $('#sys-ta'), logModeSel: $('#log-mode-select'), sysSet: $('#set-global-btn'), sysClr: $('#clear-global-btn'), warn: $('#vision-warn'), warnModel: $('#warn-model'), }; ``` -------------------------------- ### Tailwind Responsive Utilities Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html A minimal subset of Tailwind responsive utility classes used in this file, including 'hidden', 'sm:inline', 'md:flex', and 'md:hidden'. ```css .hidden { display: none !important; } @media (min-width: 640px) { .sm\:inline { display: inline !important; } } @media (min-width: 768px) { .md\:flex { display: flex !important; } .md\:hidden { display: none !important; } } ``` -------------------------------- ### Typing Dots Animation Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html CSS for a typing indicator animation using three bouncing dots. ```css .typ { display: flex; align-items: center; gap: 5px; padding: 8px 0 8px; } .typ span { width: 7px; height: 7px; background: var(--t3); border-radius: 50%; animation: typBounce 1.4s infinite ease-in-out; } .typ span:nth-child(2) { animation-delay: 0.16s; } .typ span:nth-child(3) { animation-delay: 0.32s; } @keyframes typBounce { 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; } 30% { transform: translateY(-6px); opacity: 1; } } ``` -------------------------------- ### Drag-and-Drop Overlay CSS Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html CSS for the drag-and-drop overlay that appears when a user drags a file over the main content area. ```css /* Drag-and-drop overlay */ #main.drag-over::after { content: 'Drop image, PDF or text file'; position: absolute; inset: 0; background: rgba(66, 133, 244, 0.1); border: 2px dashed var(--grad1); border-radius: var(--radius-lg); display: flex; align-items: center; justify-content: center; font-size: 16px; color: var(--grad1); font-weight: 500; pointer-events: none; z-index: 20; } #main { position: relative; } ``` -------------------------------- ### Save Log Mode Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Saves the selected log mode to server settings or local storage. ```javascript async function saveLogMode() { S.logMode = D.logModeSel.value === 'all' ? 'all' : 'errors_only'; if (IS_SERVED) { try { await fetch('/api/settings', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ logMode: S.logMode }), }); } catch {} } else { lo ``` -------------------------------- ### Persistence - Load Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Loads conversation history from the server or local storage and sanitizes it. ```javascript async function load() { if (IS_SERVED) { try { const r = await fetch('/api/chats'); if (r.ok) S.convs = await r.json(); } catch {} } else { try { const d = localStorage.getItem('g-convs'); if (d) S.convs = JSON.parse(d); } catch (e) { S.convs = []; } } // ÔöÇÔöÇ Sanitize: heal any conversation that is missing required fields // (handles old schema that used `messages` instead of `msgs`, or any // partial/corrupted entries that snuck in from a previous version) S.convs = (Array.isArray(S.convs) ? S.convs : []).map((c) => ({ id: c.id || gid(), title: c.title || 'Untitled', ts: c.ts || Date.now(), model: c.model || '', sys: c.sys || '', // migrate old `messages` key ÔåÆ `msgs`; fall back to [] msgs: Array.isArray(c.msgs) ? c.msgs : Array.isArray(c.messages) ? c.messages : [], })); } ``` -------------------------------- ### Vision Warning Styles Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html CSS for a warning message related to vision capabilities. ```css /* Vision warning */ #vision-warn { display: none; align-items: center; gap: 8px; max-width: 740px; width: 100%; margin-bottom: 8px; padding: 10px 16px; background: rgba(253, 176, 57, 0.08); border-radius: 10px; font-size: 13px; color: var(--orange); border: 1px solid rgba(253, 176, 57, 0.2); } #vision-warn.on { display: flex; } ``` -------------------------------- ### Input Area Styles Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html CSS for the main input area, including Gemini/ChatGPT style. ```css #inp-area { padding: 8px 20px 18px; flex-shrink: 0; display: flex; flex-direction: column; align-items: center; } ``` -------------------------------- ### Hardware Stats Polling Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Asynchronous function to poll hardware statistics (CPU and RAM usage) from the server. ```javascript async function pollHW() { if (!IS_SERVED) return; try { const r = await fetch('/api/stats'); if (!r.ok) return; const d = await r.json(); if (d.ram_percent === -1) return; setBar('cpu', d.cpu_percent); setBar('ram', d.ram_percent); } catch {} } ``` -------------------------------- ### Font Awesome Font Path Override Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Overrides Font Awesome font paths to use local vendor files for solid and regular styles. ```css @font-face { font-family: 'Font Awesome 6 Free'; font-style: normal; font-weight: 900; font-display: block; src: url('./vendor/fa-solid-900.woff2') format('woff2'); } @font-face { font-family: 'Font Awesome 6 Free'; font-style: normal; font-weight: 400; font-display: block; src: url('./vendor/fa-regular-400.woff2') format('woff2'); } ``` -------------------------------- ### Toasts CSS Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html CSS styles for toast notifications, including animations for appearing and disappearing. ```css /* TOASTS */ #toasts { position: fixed; bottom: 90px; left: 50%; transform: translateX(-50%); z-index: 300; display: flex; flex-direction: column; gap: 8px; align-items: center; pointer-events: none; } .toast { background: var(--tst); color: var(--t1); padding: 9px 20px; border-radius: 10px; font-size: 13px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.35); animation: toastIn 0.22s ease, toastOut 0.22s ease 2.2s forwards; white-space: nowrap; border: 1px solid var(--bd); } @keyframes toastIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } } @keyframes toastOut { from { opacity: 1; } to { opacity: 0; transform: translateY(-6px); } } ``` -------------------------------- ### Persistence - Save Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Saves the current conversation state either to the server or local storage. ```javascript let saveTimer = null; function save() { if (IS_SERVED) { clearTimeout(saveTimer); saveTimer = setTimeout( () => fetch('/api/chats', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(S.convs), }).catch(() => {}), 800, ); } else { try { localStorage.setItem('g-convs', JSON.stringify(S.convs)); } catch (e) {} } } ``` -------------------------------- ### Message Rendering Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html Renders a single message with optional actions like like/dislike and copy. ```javascript function renderMsg(msg) { const parsed = msg.content.split('\n').map(esc).join('
'); const actions = msg.id ? `
` : ''; return `
${parsed || ''}
${actions}
`; } ``` -------------------------------- ### Avatar Styling Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html CSS for styling user and AI avatars within message rows. ```css .ma { width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0; display: flex; align-items: center; justify-content: center; margin-top: 2px; } .ma.ai { background: linear-gradient( 135deg, var(--grad1), var(--grad2), var(--grad3) ); } .ma.ai svg { width: 16px; height: 16px; } .ma.u { background: var(--bg3); border: 1px solid var(--bd); font-size: 11px; font-weight: 700; color: var(--t2); } ``` -------------------------------- ### Streaming Chat Response Handling Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html This snippet demonstrates how to fetch a streaming response from the Ollama API, decode the chunks, parse JSON messages, and update the UI with the AI's content. It includes error handling for network issues and abort signals, as well as final rendering of markdown content. ```javascript s.push(am); }); try { const res = await fetch(OLLAMA + '/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: conv.model, messages: apiMsgs, stream: true, options: { temperature: parseFloat(document.getElementById('temp-input')?.value) || 0.7, }, }), signal: S.abort.signal, }); if (!res.ok) throw new Error('Ollama error ' + res.status); const reader = res.body.getReader(); const dec = new TextDecoder(); if (contentEl) contentEl.innerHTML = ''; while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = dec.decode(value, { stream: true }); for (const line of chunk.split('\n')) { if (!line.trim()) continue; try { const p = JSON.parse(line); if (p.message?.content) { aiMsg.content += p.message.content; if (contentEl) contentEl.textContent = aiMsg.content; scrollEnd(); } } catch {} } } if (contentEl) { contentEl.innerHTML = renderMd(aiMsg.content); contentEl.querySelectorAll('pre code').forEach((b) => { if (!b.classList.contains('hljs')) hljs.highlightElement(b); }); } } catch (err) { if (err.name === 'AbortError') { if (aiMsg.content) { if (contentEl) contentEl.innerHTML = renderMd(aiMsg.content); } else if (contentEl) contentEl.innerHTML = '\n[Stopped]\n'; } else { if (contentEl) contentEl.innerHTML = inaly { S.streaming = false; updateSend(); save(); scrollEnd(); setTimeout(() => D.inp.focus(), 100); } } ``` -------------------------------- ### Message Input and Action Buttons CSS Source: https://github.com/techjarves/usb-uncensored-llm/blob/main/Shared/FastChatUI.html CSS styles for the message input area and action buttons, including send and stop streaming buttons. ```css /* Text row (textarea + action buttons) */ .text-row { display: flex; align-items: flex-end; padding: 6px 8px 8px 16px; } #msg-inp { flex: 1; background: transparent; border: none; outline: none; font-family: var(--font); font-size: 15px; color: var(--t1); resize: none; max-height: 200px; line-height: 1.55; padding: 6px 4px; } #msg-inp::placeholder { color: var(--t3); } /* Action buttons row */ .ia { display: flex; align-items: center; gap: 3px; flex-shrink: 0; } /* Icon button base */ .ib2 { background: none; border: none; color: var(--t3); cursor: pointer; padding: 8px; border-radius: 50%; font-size: 15px; transition: background 0.15s, color 0.15s; display: flex; align-items: center; justify-content: center; } .ib2:hover { background: var(--bgh); color: var(--t1); } /* Send button */ .sbtn { width: 36px; height: 36px; border-radius: 50%; background: var(--bd); color: var(--bg); transition: background 0.2s, transform 0.12s; font-size: 14px; } .sbtn.on { background: var(--t1); cursor: pointer; } .sbtn.on:hover { transform: scale(1.06); } .sbtn.on:active { transform: scale(0.94); } /* Stop streaming button */ .stbtn { width: 36px; height: 36px; border-radius: 50%; background: var(--t1); color: var(--bg); cursor: pointer; font-size: 13px; transition: background 0.2s; border: none; } .stbtn:hover { opacity: 0.85; } ```