diff options
Diffstat (limited to 'chat/chat-modules.js')
| -rw-r--r-- | chat/chat-modules.js | 365 |
1 files changed, 365 insertions, 0 deletions
diff --git a/chat/chat-modules.js b/chat/chat-modules.js new file mode 100644 index 0000000..8483d48 --- /dev/null +++ b/chat/chat-modules.js @@ -0,0 +1,365 @@ +/* + * Version: 1.0.0-lite + * ChatStudio Lite - Module System + * Module CRUD, import/export, module list rendering, AI module generator. + * Module format: { id, name, desc, prompt, help, commands } + */ + +const DEFAULT_MODULES = [ + { + id: 'default', + name: 'AI Chat', + desc: 'General purpose AI assistant', + prompt: 'You are a helpful AI assistant on WebX11. You have access to these tools:\n\n1. **Search:** /api/search?q=QUERY - Searches the web via DuckDuckGo. Returns HTML results. Use this for current events, facts, and web information.\n\n2. **Fetch:** /api/fetch?url=URL - Fetches content from any URL. Use this to read web pages, APIs, or documents.\n\n3. **Commands:** Users can type /help, /search [query], /fetch [url], /explain [code], /status, /clear directly.\n\nWhen a user asks about current events, recent info, or something you dont know, suggest they use /search or tell them to ask you to search the web. If they provide a URL, offer to fetch it.\n\nBe concise, helpful, and use the available tools when appropriate.', + help: 'Just type any question! I can help with:\n- Current events (I can search the web)\n- Web pages (I can fetch URLs)\n- Coding questions\n- General knowledge\n- Problem solving\n\nTry: /search latest news', + commands: '/help, /search, /fetch, /explain, /status, /clear' + }, + { + id: 'search', + name: 'Search', + desc: 'Search the web for information', + prompt: 'You are a web search assistant. When asked about current events, recent information, or things you dont know, suggest searching the web. Provide helpful search suggestions.', + help: 'Use /search [query] to get search suggestions.\n\nExample: /search Python tutorials', + commands: '/search, /find' + }, + { + id: 'fetch', + name: 'URL Fetch', + desc: 'Fetch content from URLs', + prompt: 'You can fetch content from URLs using the fetch tool. When a user provides a URL, use the fetch tool to get its content.', + help: 'Provide a URL and I can fetch its content.\n\nJust paste any URL in the chat!', + commands: '/fetch, /get' + }, + { + id: 'summarize', + name: 'Summarizer', + desc: 'Summarize long text', + prompt: 'You are a text summarization expert. When given text, provide a clear summary highlighting the key points.', + help: 'Paste long text and Ill summarize it.\n\nUse /summary for a brief overview.', + commands: '/summarize, /summary' + }, + { + id: 'plan', + name: 'Planner', + desc: 'Create plans and todo lists', + prompt: 'You are a planning assistant. Help users break down tasks into steps, create schedules, and organize work.', + help: 'Tell me what you want to accomplish and Ill help create a plan!\n\nCommands:\n- /plan [goal] - Create a plan\n- /todo - Show current todos', + commands: '/plan, /todo' + }, + { + id: 'code', + name: 'Code Helper', + desc: 'Programming assistance', + prompt: 'You are a coding expert. Help with code explanations, debugging, writing, and best practices. Provide clear, working code examples.', + help: 'I can help with:\n- Explaining code\n- Debugging errors\n- Writing new code\n- Code reviews\n\nJust paste your code and ask!', + commands: '/code, /debug, /explain' + }, + { + id: 'translate', + name: 'Translator', + desc: 'Translate between languages', + prompt: 'You are a multilingual translator. Translate text between languages accurately while preserving meaning.', + help: 'Translate text by specifying languages:\n\n- /translate [text] to [language]\n- /es Hello - translate to Spanish\n- /en Hola - translate to English', + commands: '/translate, /es, /fr, /de, /zh' + }, + { + id: 'webx11-help', + name: 'WebX11 Help', + desc: 'Help with WebX11 Spaces', + prompt: 'You are a WebX11 Spaces help assistant. Help users with:\n- Creating and managing their web space\n- Using the dashboard\n- Uploading files\n- Custom domains\n- Module system\n- Any technical questions', + help: 'WebX11 Spaces Help:\n\n- Dashboard: Upload & manage files\n- Register: Create your space\n- Chat: AI assistant\n- Modules: Specialized tools\n\nType /help for more!', + commands: '/help, /webx11, /space' + } +]; + +var modules = []; +var currentModule = 'default'; +var editingModuleId = null; + +var STORAGE_KEY = 'webx11_chat_modules'; + +function loadModules() { + try { + var saved = localStorage.getItem(STORAGE_KEY); + if (saved) { + modules = JSON.parse(b64_to_utf8(saved)); + } else { + modules = JSON.parse(JSON.stringify(DEFAULT_MODULES)); + saveModules(); + } + } catch (e) { + console.warn('Failed to load modules:', e); + modules = JSON.parse(JSON.stringify(DEFAULT_MODULES)); + } + renderModuleList(); + updateHelpPanel(); +} + +function saveModules() { + try { + var encoded = utf8_to_b64(JSON.stringify(modules)); + localStorage.setItem(STORAGE_KEY, encoded); + } catch (e) { + console.warn('Failed to save modules:', e); + } +} + +function renderModuleList() { + var list = document.getElementById('moduleList'); + list.innerHTML = modules.map(function (m) { + var isActive = m.id === currentModule; + var actions = m.id !== 'default' + ? '<div class="module-actions">' + + '<button class="module-action-btn" onclick="event.stopPropagation(); copyModule(\'' + escapeHtml(m.id) + '\')" title="Copy">Copy</button>' + + '<button class="module-action-btn delete" onclick="event.stopPropagation(); deleteModule(\'' + escapeHtml(m.id) + '\')" title="Delete">Del</button>' + + '</div>' + : ''; + return '<div class="module-item' + (isActive ? ' active' : '') + '" onclick="switchModule(\'' + escapeHtml(m.id) + '\')" oncontextmenu="moduleContextMenu(event, \'' + escapeHtml(m.id) + '\')">' + + '<div class="module-name">' + escapeHtml(m.name) + '</div>' + + '<div class="module-desc">' + escapeHtml(m.desc) + '</div>' + + actions + + '</div>'; + }).join(''); +} + +function getCurrentModule() { + return modules.find(function (m) { return m.id === currentModule; }); +} + +function switchModule(id) { + currentModule = id; + renderModuleList(); + updateHelpPanel(); + var found = modules.find(function (m) { return m.id === id; }); + showToast('Switched to ' + (found ? found.name : 'module')); + addLog('Module', 'Switched to ' + (found ? found.name : id)); +} + +function updateHelpPanel() { + var mod = modules.find(function (m) { return m.id === currentModule; }); + if (mod) { + document.getElementById('helpTitle').textContent = mod.name; + document.getElementById('helpText').textContent = mod.help || 'No help available.'; + var cmds = mod.commands ? mod.commands.split(',').map(function (c) { return c.trim(); }) : []; + document.getElementById('helpCommands').innerHTML = cmds.map(function (c) { + return '<span class="help-cmd">' + escapeHtml(c) + '</span>'; + }).join(''); + } +} + +function moduleContextMenu(e, id) { + e.preventDefault(); + if (id === 'default') return; + var action = prompt('Module actions: edit, copy, delete (or cancel)'); + if (!action) return; + var a = action.toLowerCase().trim(); + if (a === 'edit') { + editModule(id); + } else if (a === 'copy') { + copyModule(id); + } else if (a === 'delete') { + deleteModule(id); + } +} + +function copyModule(id) { + var mod = modules.find(function (m) { return m.id === id; }); + if (!mod) return; + var newId = id + '_copy_' + Date.now(); + var newModule = JSON.parse(JSON.stringify(mod)); + newModule.id = newId; + newModule.name = mod.name + ' (Copy)'; + modules.push(newModule); + saveModules(); + renderModuleList(); + showToast('Module copied!'); +} + +function deleteModule(id) { + if (!confirm('Delete this module?')) return; + modules = modules.filter(function (m) { return m.id !== id; }); + if (currentModule === id) { + currentModule = 'default'; + } + saveModules(); + renderModuleList(); + updateHelpPanel(); + showToast('Module deleted'); +} + +function editModule(id) { + openModuleModal(id); +} + +function openModuleModal(editId) { + editId = editId || null; + editingModuleId = editId; + document.getElementById('moduleModalTitle').textContent = editId ? 'Edit Module' : 'Add Module'; + + if (editId) { + var mod = modules.find(function (m) { return m.id === editId; }); + if (mod) { + document.getElementById('moduleName').value = mod.name; + document.getElementById('moduleDesc').value = mod.desc; + document.getElementById('modulePrompt').value = mod.prompt; + document.getElementById('moduleHelp').value = mod.help; + document.getElementById('moduleCommands').value = mod.commands; + } + } else { + document.getElementById('moduleName').value = ''; + document.getElementById('moduleDesc').value = ''; + document.getElementById('modulePrompt').value = ''; + document.getElementById('moduleHelp').value = ''; + document.getElementById('moduleCommands').value = ''; + } + + document.getElementById('aiModuleDesc').value = ''; + document.getElementById('moduleModal').classList.add('open'); +} + +function closeModuleModal() { + document.getElementById('moduleModal').classList.remove('open'); + editingModuleId = null; +} + +function saveModule() { + var name = document.getElementById('moduleName').value.trim(); + var desc = document.getElementById('moduleDesc').value.trim(); + var prompt = document.getElementById('modulePrompt').value.trim(); + var help = document.getElementById('moduleHelp').value.trim(); + var commands = document.getElementById('moduleCommands').value.trim(); + + if (!name) { + showToast('Please enter a module name'); + return; + } + + if (editingModuleId) { + var idx = modules.findIndex(function (m) { return m.id === editingModuleId; }); + if (idx !== -1) { + modules[idx] = { + id: modules[idx].id, + name: name, + desc: desc, + prompt: prompt, + help: help, + commands: commands + }; + } + } else { + var id = name.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, ''); + modules.push({ + id: id, + name: name, + desc: desc, + prompt: prompt, + help: help, + commands: commands + }); + } + + saveModules(); + renderModuleList(); + closeModuleModal(); + showToast(editingModuleId ? 'Module updated!' : 'Module added!'); +} + +// === AI Module Generator === + +async function generateModuleWithAI() { + var desc = document.getElementById('aiModuleDesc').value.trim(); + if (!desc) { + showToast('Please describe what the module should do'); + return; + } + + var btn = event.target; + btn.disabled = true; + btn.textContent = 'Generating...'; + + try { + var headers = { 'Content-Type': 'application/json' }; + if (typeof OLLAMA_API_KEY !== 'undefined' && OLLAMA_API_KEY) { + headers['Authorization'] = 'Bearer ' + OLLAMA_API_KEY; + } + + var response = await fetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/generate', { + method: 'POST', + headers: headers, + body: JSON.stringify({ + model: document.getElementById('modelSelect').value, + prompt: 'Generate a chat module specification for WebX11 AI Chat.\n\nModule description: ' + desc + '\n\nRespond with a JSON object containing:\n{\n "name": "Module Name",\n "desc": "Short description (5-10 words)",\n "prompt": "System prompt/instructions for the AI (2-3 sentences)",\n "help": "How to use this module (3-5 bullet points)",\n "commands": "comma-separated commands, at least 3"\n}\n\nOnly respond with valid JSON, no other text.', + stream: false + }) + }); + + if (!response.ok) throw new Error('AI request failed'); + + var data = await response.json(); + var result = JSON.parse(data.response); + + document.getElementById('moduleName').value = result.name || ''; + document.getElementById('moduleDesc').value = result.desc || ''; + document.getElementById('modulePrompt').value = result.prompt || ''; + document.getElementById('moduleHelp').value = result.help || ''; + document.getElementById('moduleCommands').value = result.commands || ''; + + showToast('Module generated! Review and save.'); + } catch (e) { + console.error('Generation error:', e); + showToast('Failed to generate: ' + e.message); + } finally { + btn.disabled = false; + btn.textContent = 'Generate with AI'; + } +} + +// === Import / Export === + +function exportModules() { + var data = JSON.stringify(modules, null, 2); + var blob = new Blob([data], { type: 'application/json' }); + var url = URL.createObjectURL(blob); + var a = document.createElement('a'); + a.href = url; + a.download = 'webx11-modules.json'; + a.click(); + URL.revokeObjectURL(url); + showToast('Modules exported!'); +} + +function importModules() { + var input = document.createElement('input'); + input.type = 'file'; + input.accept = '.json'; + input.onchange = async function (e) { + var file = e.target.files[0]; + if (!file) return; + try { + var text = await file.text(); + var imported = JSON.parse(text); + if (Array.isArray(imported)) { + for (var i = 0; i < imported.length; i++) { + var m = imported[i]; + m.id = m.id + '_imported'; + modules.push(m); + } + saveModules(); + renderModuleList(); + updateHelpPanel(); + showToast('Modules imported!'); + } else { + showToast('Invalid format'); + } + } catch (err) { + showToast('Import failed: ' + err.message); + } + }; + input.click(); +} + + +// Load modules from storage (runs after core.js) + +// Auto-init once DOM + deps are ready +document.addEventListener('DOMContentLoaded', function() { loadModules(); }); |