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 21f9ef8..a39d8d9 100644
--- a/js/chat/chat-core.js
+++ b/js/chat/chat-core.js
@@ -566,17 +566,16 @@ 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 () {
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,47 @@ document.addEventListener("DOMContentLoaded", () => {
nologo: true,
safe: true
});
+ imageUrls.push(url);
} catch (e) {
console.warn('polliLib generateImageUrl failed', e);
- return match;
}
+ 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();
}
window.addNewMessage({ role: "ai", content: aiContent, imageUrls, audioUrls });
diff --git a/js/chat/chat-init.js b/js/chat/chat-init.js
index bc8d6c8..9ba933e 100644
--- a/js/chat/chat-init.js
+++ b/js/chat/chat-init.js
@@ -32,37 +32,8 @@ 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") {
+ 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 cdac97f..266d5f7 100644
--- a/js/chat/chat-storage.js
+++ b/js/chat/chat-storage.js
@@ -54,37 +54,8 @@ 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") {
+ 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 4c32ce0..041f36a 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.
---
@@ -25,33 +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]
+```
-Only return code when explicitly asked.
+- Always specify the language after the opening backticks.
+- Return code only when asked.
+- If both code and media are requested, include all needed blocks.
-Do not send images when only code is requested.
+---
+
+## Images
+
+- Do not include external URLs.
+- 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:
-If both code and image are requested, include both.
+```audio
+welcome to unity
+```
-Images
+- Audio prompts are hidden from chat and rendered as playable audio clips.
-Do not include external URLs.
+---
+
+## UI Commands
-When an image is requested, start a new line with image: followed by a concise descriptive prompt.
+- Request interface actions inside a `ui` fenced block:
-Example:
-image: a glowing neon cityscape at night with flying cars
+```ui
+open the screensaver
+```
-General Guidelines
+- Use one command per block; commands run silently without being shown.
-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 `[memory]` blocks and fenced `image`, `audio`, and `ui` sections.
+- Stay consistent and predictable in output formatting.
+- If uncertain, prioritize clarity and brevity.