diff options
Diffstat (limited to 'chat')
| -rw-r--r-- | chat/chat-commands.js | 28 | ||||
| -rw-r--r-- | chat/chat-modules.js | 20 | ||||
| -rw-r--r-- | chat/chat-ui.html | 2 |
3 files changed, 33 insertions, 17 deletions
diff --git a/chat/chat-commands.js b/chat/chat-commands.js index d5cfdd9..c0c77ee 100644 --- a/chat/chat-commands.js +++ b/chat/chat-commands.js @@ -4,6 +4,16 @@ * Handles /search, /fetch, /explain, /help, /status, /clear, /modules, etc. */ +// Fetch with timeout — AbortController-based, 30s default +function _csFetch(url, opts, timeout) { + timeout = timeout || 30000; + opts = opts || {}; + var ctrl = new AbortController(); + opts.signal = ctrl.signal; + var timer = setTimeout(function() { ctrl.abort(); }, timeout); + return fetch(url, opts).finally(function() { clearTimeout(timer); }); +} + async function handleCommand(cmd) { var parts = cmd.split(' '); var command = parts[0].toLowerCase(); @@ -67,7 +77,7 @@ async function handleCommand(cmd) { 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', { + var response = await _csFetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/generate', { method: 'POST', headers: headers, body: JSON.stringify({ @@ -95,7 +105,7 @@ async function handleCommand(cmd) { } showTyping(); try { - var response = await fetch('/api/search?q=' + encodeURIComponent(query) + '&summarize=true'); + var response = await _csFetch('/api/search?q=' + encodeURIComponent(query) + '&summarize=true'); var data = await response.json(); hideTyping(); if (data.error) { @@ -140,11 +150,11 @@ async function handleCommand(cmd) { } showTyping(); try { - var extractResp = await fetch('/api/extract?url=' + encodeURIComponent(url)); + var extractResp = await _csFetch('/api/extract?url=' + encodeURIComponent(url)); var extractOk = extractResp.ok; var extractData = extractOk ? await extractResp.json() : null; - var htmlResp = await fetch('/api/fetch?url=' + encodeURIComponent(url)); + var htmlResp = await _csFetch('/api/fetch?url=' + encodeURIComponent(url)); if (!htmlResp.ok) throw new Error('HTTP ' + htmlResp.status); var html = await htmlResp.text(); var htmlPreview = html.substring(0, 262144) + (html.length > 262144 ? '\n\n... (truncated)' : ''); @@ -178,7 +188,7 @@ async function handleCommand(cmd) { } showTyping(); try { - var response = await fetch('/api/summarize?url=' + encodeURIComponent(url)); + var response = await _csFetch('/api/summarize?url=' + encodeURIComponent(url)); if (!response.ok) throw new Error('HTTP ' + response.status); var data = await response.json(); hideTyping(); @@ -204,7 +214,7 @@ async function handleCommand(cmd) { } showTyping(); try { - var response = await fetch('/api/extract?url=' + encodeURIComponent(url)); + var response = await _csFetch('/api/extract?url=' + encodeURIComponent(url)); if (!response.ok) throw new Error('HTTP ' + response.status); var data = await response.json(); hideTyping(); @@ -225,7 +235,7 @@ async function handleCommand(cmd) { // === /status === if (command === '/status') { try { - var response = await fetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/tags'); + var response = await _csFetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/tags'); var data = await response.json(); var modelCount = data.models ? data.models.length : 0; var current = document.getElementById('modelSelect').value; @@ -241,7 +251,7 @@ async function handleCommand(cmd) { showTyping(); try { var start = Date.now(); - var response = await fetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/tags'); + var response = await _csFetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/tags'); var ms = Date.now() - start; hideTyping(); addMessage('ai', '**Connection Test**\n\n- Response time: ' + ms + 'ms\n- Status: OK'); @@ -255,7 +265,7 @@ async function handleCommand(cmd) { // === /models or /list models === if (command === '/models') { try { - var response = await fetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/tags'); + var response = await _csFetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/tags'); var data = await response.json(); var list = data.models ? data.models.map(function (m) { return '- ' + m.name; }).join('\n') : 'No models'; addMessage('ai', '**Available Models:**\n\n' + list); diff --git a/chat/chat-modules.js b/chat/chat-modules.js index 140e379..295c7c9 100644 --- a/chat/chat-modules.js +++ b/chat/chat-modules.js @@ -10,9 +10,9 @@ 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' + prompt: 'You are a helpful AI assistant on WebX11 newOS4. Current date is injected dynamically. You have access to these tools:\n\n1. **Search:** /api/search?q=QUERY - Searches the web via DuckDuckGo/SearXNG. Returns results with titles, URLs, and snippets. Use this for current events, facts, and web information.\n2. **Fetch:** /api/fetch?url=URL - Fetches raw HTML from any URL. Use this to read web pages, APIs, or documents.\n3. **Extract:** /api/extract?url=URL - Extracts readable content (title, word count, excerpt, full text) from a URL. Prefer this over /fetch for reading articles.\n4. **Summarize:** /api/summarize?url=URL - AI summarization of a URL.\n\n**Tool-Use Rules:**\n- NEVER announce that you are about to use a tool (no "Hold on...", "Let me search...", "Fetching...", or waiting indicators). Just use the tool and present the result immediately.\n- If a /search returns generic/portal results (e.g., google.com homepages), automatically retry with a more specific query using site: operators or exact phrases in quotes.\n- If a user types a malformed command (e.g., /extraxt instead of /extract, /serch instead of /search), recognize their intent and suggest the correct command immediately: "Did you mean /extract [url]? Here are the available commands: /search, /fetch, /extract, /summarize"\n- When a user shares a URL, proactively suggest /extract or /fetch with the URL pre-filled.\n- Be concise. Deliver results, not commentary about the process.\n\n**Available slash commands:** /help, /search [query], /fetch [url], /extract [url], /summarize [url], /explain [code], /status, /models, /clear', + help: 'Just type any question! I can help with:\n- Current events (I can search the web)\n- Web pages (I can extract/fetch URLs)\n- Coding questions\n- General knowledge\n- Problem solving\n\nCommands: /search, /fetch, /extract, /summarize, /explain, /status, /clear\n\nTry: /search latest news', + commands: '/help, /search, /fetch, /extract, /summarize, /explain, /status, /models, /clear' }, { id: 'search', @@ -72,7 +72,8 @@ const DEFAULT_MODULES = [ } ]; -var modules = []; +// modules array lives on window.modules (set by _initLiteEngine in module-chatstudio.js) +// Do NOT redeclare here — just reference the global var currentModule = 'default'; var editingModuleId = null; @@ -266,14 +267,19 @@ function saveModule() { // === AI Module Generator === -async function generateModuleWithAI() { +async function generateModuleWithAI(evt) { + var e = evt || window.event; var desc = document.getElementById('aiModuleDesc').value.trim(); if (!desc) { showToast('Please describe what the module should do'); return; } - var btn = event.target; + var btn = e ? e.target : null; + if (!btn) { + var modal = document.getElementById('moduleModal'); + btn = modal ? modal.querySelector('button[onclick*="generateModule"]') : null; + } btn.disabled = true; btn.textContent = 'Generating...'; @@ -283,7 +289,7 @@ async function generateModuleWithAI() { headers['Authorization'] = 'Bearer ' + OLLAMA_API_KEY; } - var response = await fetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/generate', { + var response = await _csFetch((typeof OLLAMA_URL !== 'undefined' ? OLLAMA_URL : '/ollama') + '/api/generate', { method: 'POST', headers: headers, body: JSON.stringify({ diff --git a/chat/chat-ui.html b/chat/chat-ui.html index efe0347..5b8e421 100644 --- a/chat/chat-ui.html +++ b/chat/chat-ui.html @@ -62,7 +62,7 @@ </div> <div class="input-area"> - <textarea id="chatInput" placeholder="Type your message..." autocomplete="off" rows="1" onfocus="this.style.height='80px'" oninput="autoExpand(this);if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();sendMessage();}"></textarea> + <textarea id="chatInput" placeholder="Type your message..." autocomplete="off" rows="1" onfocus="this.style.height='80px'" onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();sendMessage();}" oninput="autoExpand(this)"></textarea> <button class="btn btn-primary" onclick="sendMessage()">Send</button> </div> </main> |