aboutsummaryrefslogtreecommitdiff
path: root/chat
diff options
context:
space:
mode:
Diffstat (limited to 'chat')
-rw-r--r--chat/chat-commands.js28
-rw-r--r--chat/chat-modules.js20
-rw-r--r--chat/chat-ui.html2
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>