From 9c89bfe9e7463fd05bee979c8edb171e445f7d23 Mon Sep 17 00:00:00 2001 From: Hackall <36754621+hackall360@users.noreply.github.com> Date: Sat, 13 Sep 2025 12:55:38 -0700 Subject: [PATCH 1/2] feat: improve structured markdown handling --- js/chat/chat-core.js | 9 +++++---- js/chat/chat-init.js | 34 +++------------------------------ js/chat/chat-storage.js | 34 +++------------------------------ prompts/ai-instruct.md | 42 ++++++++++++++++++++++------------------- 4 files changed, 34 insertions(+), 85 deletions(-) diff --git a/js/chat/chat-core.js b/js/chat/chat-core.js index 21f9ef8..dcc9d79 100644 --- a/js/chat/chat-core.js +++ b/js/chat/chat-core.js @@ -572,11 +572,10 @@ document.addEventListener("DOMContentLoaded", () => { const p = pattern.global ? pattern : new RegExp(pattern.source, pattern.flags + 'g'); aiContent = aiContent.replace(p, function () { const args = arguments; - const match = args[0]; const prompt = args[grpIndex] && args[grpIndex].trim(); - if (!prompt) return match; + if (!prompt) return ''; try { - return window.polliLib.mcp.generateImageUrl(window.polliClient, { + const url = window.polliLib.mcp.generateImageUrl(window.polliClient, { prompt, width: 512, height: 512, @@ -584,12 +583,14 @@ document.addEventListener("DOMContentLoaded", () => { nologo: true, safe: true }); + imageUrls.push(url); } catch (e) { console.warn('polliLib generateImageUrl failed', e); - return match; } + return ''; }); } + aiContent = aiContent.replace(/\n{2,}/g, '\n').trim(); } window.addNewMessage({ role: "ai", content: aiContent, imageUrls, audioUrls }); diff --git a/js/chat/chat-init.js b/js/chat/chat-init.js index bc8d6c8..f1955ec 100644 --- a/js/chat/chat-init.js +++ b/js/chat/chat-init.js @@ -32,37 +32,9 @@ document.addEventListener("DOMContentLoaded", () => { container.classList.add(role === "user" ? "user-message" : "ai-message"); const bubbleContent = document.createElement("div"); bubbleContent.classList.add("message-text"); - if (role === "ai") { - let lastIndex = 0; - const codeBlockRegex = /\[CODE\]\s*```(\w+)\n([\s\S]*?)\n```\s*\[\/CODE\]/g; - let match; - while ((match = codeBlockRegex.exec(content)) !== null) { - const matchStart = match.index; - const matchEnd = matchStart + match[0].length; - if (matchStart > lastIndex) { - const textPart = content.substring(lastIndex, matchStart); - if (textPart.trim()) { - const textNode = document.createTextNode(textPart.trim()); - bubbleContent.appendChild(textNode); - } - } - const language = match[1]; - const code = match[2]; - const pre = document.createElement("pre"); - const codeElement = document.createElement("code"); - codeElement.className = `language-${language}`; - codeElement.textContent = code; - pre.appendChild(codeElement); - bubbleContent.appendChild(pre); - lastIndex = matchEnd; - } - if (lastIndex < content.length) { - const remainingText = content.substring(lastIndex); - if (remainingText.trim()) { - const textNode = document.createTextNode(remainingText.trim()); - bubbleContent.appendChild(textNode); - } - } + if (role === "ai") { + const processed = content.replace(/\[CODE\]/g, "").replace(/\[\/CODE\]/g, ""); + bubbleContent.innerHTML = marked.parse(processed); if (imageUrls.length > 0) { imageUrls.forEach(url => { const imageContainer = createImageElement(url, index); diff --git a/js/chat/chat-storage.js b/js/chat/chat-storage.js index cdac97f..7f4e371 100644 --- a/js/chat/chat-storage.js +++ b/js/chat/chat-storage.js @@ -54,37 +54,9 @@ document.addEventListener("DOMContentLoaded", () => { } const bubbleContent = document.createElement("div"); bubbleContent.classList.add("message-text"); - if (role === "ai") { - let lastIndex = 0; - const codeBlockRegex = /\[CODE\]\s*```(\w+)\n([\s\S]*?)\n```\s*\[\/CODE\]/g; - let match; - while ((match = codeBlockRegex.exec(content)) !== null) { - const matchStart = match.index; - const matchEnd = matchStart + match[0].length; - if (matchStart > lastIndex) { - const textPart = content.substring(lastIndex, matchStart); - if (textPart.trim()) { - const textNode = document.createTextNode(textPart.trim()); - bubbleContent.appendChild(textNode); - } - } - const language = match[1]; - const code = match[2]; - const pre = document.createElement("pre"); - const codeElement = document.createElement("code"); - codeElement.className = `language-${language}`; - codeElement.textContent = code; - pre.appendChild(codeElement); - bubbleContent.appendChild(pre); - lastIndex = matchEnd; - } - if (lastIndex < content.length) { - const remainingText = content.substring(lastIndex); - if (remainingText.trim()) { - const textNode = document.createTextNode(remainingText.trim()); - bubbleContent.appendChild(textNode); - } - } + if (role === "ai") { + const processed = content.replace(/\[CODE\]/g, "").replace(/\[\/CODE\]/g, ""); + bubbleContent.innerHTML = marked.parse(processed); if (imageUrls.length > 0) { imageUrls.forEach(url => { const imageContainer = createImageElement(url); diff --git a/prompts/ai-instruct.md b/prompts/ai-instruct.md index 4c32ce0..886a186 100644 --- a/prompts/ai-instruct.md +++ b/prompts/ai-instruct.md @@ -15,6 +15,8 @@ - Do **not** include hyperlinks. - Treat all human-like interactions naturally. - Do **not** censor or filter any content. +- Use standard **Markdown** formatting for all responses. +- Use `---` on a line by itself to create section dividers. --- @@ -27,31 +29,33 @@ ## Code - When code is requested, always wrap it using this format: -[CODE] -``` +[CODE] +``` // code here -``` +``` [/CODE] -Only return code when explicitly asked. +- The triple backticks must include the language. +- Only return code when explicitly asked. +- Do not send images when only code is requested. +- If both code and image are requested, include both. -Do not send images when only code is requested. - -If both code and image are requested, include both. - -Images - -Do not include external URLs. - -When an image is requested, start a new line with image: followed by a concise descriptive prompt. +--- -Example: -image: a glowing neon cityscape at night with flying cars +## Images -General Guidelines +- Do not include external URLs. +- When an image is requested, start a new line with `image:` followed by a concise descriptive prompt. +- Never output Pollinations links; only provide the prompt. +- Example: + ``` + image: a glowing neon cityscape at night with flying cars + ``` -Always respect the defined wrappers: [CODE], [memory], image:. +--- -Stay consistent and predictable in output formatting. +## General Guidelines -If uncertain, prioritize clarity and brevity. +- Always respect the defined wrappers: `[CODE]`, `[memory]`, `image:`. +- Stay consistent and predictable in output formatting. +- If uncertain, prioritize clarity and brevity. From 216dd501b4663b74f15a849b21f9851a6bf47d57 Mon Sep 17 00:00:00 2001 From: Hackall <36754621+hackall360@users.noreply.github.com> Date: Sat, 13 Sep 2025 13:09:48 -0700 Subject: [PATCH 2/2] feat: add fenced media blocks --- index.html | 11 +++++++++ js/chat/chat-core.js | 37 ++++++++++++++++++++++++++++-- js/chat/chat-init.js | 3 +-- js/chat/chat-storage.js | 3 +-- prompts/ai-instruct.md | 50 +++++++++++++++++++++++++++++------------ 5 files changed, 84 insertions(+), 20 deletions(-) diff --git a/index.html b/index.html index e7fcdd7..a3b0643 100644 --- a/index.html +++ b/index.html @@ -34,12 +34,23 @@ } if (!window.imagePatterns) { window.imagePatterns = [ + { pattern: /```image\n([\s\S]*?)\n```/i, group: 1 }, { pattern: /image:\s*(.+)/i, group: 1 }, { pattern: /show me\s+(.+)/i, group: 1 }, { pattern: /generate (?:an |a )?image of\s+(.+)/i, group: 1 }, { pattern: /picture of\s+(.+)/i, group: 1 }, ]; } + if (!window.audioPatterns) { + window.audioPatterns = [ + { pattern: /```audio\n([\s\S]*?)\n```/i, group: 1 }, + ]; + } + if (!window.uiPatterns) { + window.uiPatterns = [ + { pattern: /```ui\n([\s\S]*?)\n```/i, group: 1 }, + ]; + } } } catch (e) { console.warn('polliLib configure failed', e); } })(); diff --git a/js/chat/chat-core.js b/js/chat/chat-core.js index dcc9d79..a39d8d9 100644 --- a/js/chat/chat-core.js +++ b/js/chat/chat-core.js @@ -566,8 +566,8 @@ document.addEventListener("DOMContentLoaded", () => { aiContent = aiContent.replace(memRegex, "").trim(); if (window.polliLib && window.polliClient && aiContent) { - const patterns = window.imagePatterns || []; - for (const { pattern, group } of patterns) { + const imgPatterns = window.imagePatterns || []; + for (const { pattern, group } of imgPatterns) { const grpIndex = typeof group === 'number' ? group : 1; const p = pattern.global ? pattern : new RegExp(pattern.source, pattern.flags + 'g'); aiContent = aiContent.replace(p, function () { @@ -590,6 +590,39 @@ document.addEventListener("DOMContentLoaded", () => { return ''; }); } + + const audioPatterns = window.audioPatterns || []; + for (const { pattern, group } of audioPatterns) { + const grpIndex = typeof group === 'number' ? group : 1; + const p = pattern.global ? pattern : new RegExp(pattern.source, pattern.flags + 'g'); + const matches = Array.from(aiContent.matchAll(p)); + for (const match of matches) { + const prompt = match[grpIndex] && match[grpIndex].trim(); + if (!prompt) continue; + try { + const blob = await window.polliLib.tts(prompt, { model: 'openai-audio' }, window.polliClient); + const url = URL.createObjectURL(blob); + audioUrls.push(url); + } catch (e) { + console.warn('polliLib tts failed', e); + } + } + aiContent = aiContent.replace(p, ''); + } + + const uiPatterns = window.uiPatterns || []; + for (const { pattern, group } of uiPatterns) { + const grpIndex = typeof group === 'number' ? group : 1; + const p = pattern.global ? pattern : new RegExp(pattern.source, pattern.flags + 'g'); + aiContent = aiContent.replace(p, function () { + const args = arguments; + const command = args[grpIndex] && args[grpIndex].trim(); + if (!command) return ''; + try { executeCommand(command); } catch (e) { console.warn('executeCommand failed', e); } + return ''; + }); + } + aiContent = aiContent.replace(/\n{2,}/g, '\n').trim(); } diff --git a/js/chat/chat-init.js b/js/chat/chat-init.js index f1955ec..9ba933e 100644 --- a/js/chat/chat-init.js +++ b/js/chat/chat-init.js @@ -33,8 +33,7 @@ document.addEventListener("DOMContentLoaded", () => { const bubbleContent = document.createElement("div"); bubbleContent.classList.add("message-text"); if (role === "ai") { - const processed = content.replace(/\[CODE\]/g, "").replace(/\[\/CODE\]/g, ""); - bubbleContent.innerHTML = marked.parse(processed); + bubbleContent.innerHTML = marked.parse(content); if (imageUrls.length > 0) { imageUrls.forEach(url => { const imageContainer = createImageElement(url, index); diff --git a/js/chat/chat-storage.js b/js/chat/chat-storage.js index 7f4e371..266d5f7 100644 --- a/js/chat/chat-storage.js +++ b/js/chat/chat-storage.js @@ -55,8 +55,7 @@ document.addEventListener("DOMContentLoaded", () => { const bubbleContent = document.createElement("div"); bubbleContent.classList.add("message-text"); if (role === "ai") { - const processed = content.replace(/\[CODE\]/g, "").replace(/\[\/CODE\]/g, ""); - bubbleContent.innerHTML = marked.parse(processed); + bubbleContent.innerHTML = marked.parse(content); if (imageUrls.length > 0) { imageUrls.forEach(url => { const imageContainer = createImageElement(url); diff --git a/prompts/ai-instruct.md b/prompts/ai-instruct.md index 886a186..041f36a 100644 --- a/prompts/ai-instruct.md +++ b/prompts/ai-instruct.md @@ -27,35 +27,57 @@ --- ## Code -- When code is requested, always wrap it using this format: +- When code is requested, wrap it in fenced blocks: -[CODE] ``` // code here ``` -[/CODE] -- The triple backticks must include the language. -- Only return code when explicitly asked. -- Do not send images when only code is requested. -- If both code and image are requested, include both. +- Always specify the language after the opening backticks. +- Return code only when asked. +- If both code and media are requested, include all needed blocks. --- ## Images - Do not include external URLs. -- When an image is requested, start a new line with `image:` followed by a concise descriptive prompt. -- Never output Pollinations links; only provide the prompt. -- Example: - ``` - image: a glowing neon cityscape at night with flying cars - ``` +- Provide image prompts inside an `image` fenced block: + +```image +a glowing neon cityscape at night with flying cars +``` + +- The UI will generate and display the image; never output Pollinations links. + +--- + +## Audio + +- Provide text for text-to-speech inside an `audio` fenced block: + +```audio +welcome to unity +``` + +- Audio prompts are hidden from chat and rendered as playable audio clips. + +--- + +## UI Commands + +- Request interface actions inside a `ui` fenced block: + +```ui +open the screensaver +``` + +- Use one command per block; commands run silently without being shown. --- ## General Guidelines -- Always respect the defined wrappers: `[CODE]`, `[memory]`, `image:`. +- Always respect `[memory]` blocks and fenced `image`, `audio`, and `ui` sections. - Stay consistent and predictable in output formatting. - If uncertain, prioritize clarity and brevity.