Upload 4 new, 6 modified files from chat-switchboard-v0.5.1.zip

This commit is contained in:
2026-02-18 20:58:13 +00:00
parent 490fe5c6a3
commit 7075538702
8 changed files with 309 additions and 55 deletions

View File

@@ -425,46 +425,88 @@ const UI = {
// ── Formatting ───────────────────────────────
/**
* Format message content with markdown rendering.
* Uses marked.js + DOMPurify when available, regex fallback otherwise.
*/
function formatMessage(content) {
if (!content) return '';
const thinkingBlocks = [];
// ── Extract thinking blocks (both <thinking> and <think> tags) ──
const thinkingBlocks = [];
let text = content;
// Extract thinking blocks
if (App.settings.showThinking) {
text = text.replace(/<thinking>([\s\S]*?)<\/thinking>/gi, (_, inner) => {
const id = 'think-' + Math.random().toString(36).slice(2, 9);
thinkingBlocks.push({ id, content: inner.trim() });
return `__THINK_${id}__`;
});
} else {
text = text.replace(/<thinking>[\s\S]*?<\/thinking>/gi, '');
}
// Escape HTML
text = esc(text);
// Restore thinking blocks (single-escaped)
for (const b of thinkingBlocks) {
const inner = esc(b.content).replace(/\n/g, '<br>');
text = text.replace(`__THINK_${b.id}__`,
`<details class="thinking-block"><summary>💭 Thinking</summary><div class="thinking-content">${inner}</div></details>`);
}
// Code blocks
text = text.replace(/```(\w*)\n?([\s\S]*?)```/g, (_, lang, code) => {
const id = 'code-' + Math.random().toString(36).slice(2, 9);
return `<pre><code id="${id}" class="lang-${lang}">${code.trim()}</code><button class="copy-code-btn" onclick="navigator.clipboard.writeText(document.getElementById('${id}').textContent).then(()=>UI.toast('Copied','success'))">Copy</button></pre>`;
// Pull out thinking blocks before any processing
text = text.replace(/<(?:thinking|think)>([\s\S]*?)<\/(?:thinking|think)>/gi, (_, inner) => {
const id = 'think-' + Math.random().toString(36).slice(2, 9);
thinkingBlocks.push({ id, content: inner.trim() });
return `THINK_PLACEHOLDER_${id}`;
});
// Inline formatting
text = text.replace(/`([^`]+)`/g, '<code>$1</code>');
text = text.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
text = text.replace(/\*([^*]+)\*/g, '<em>$1</em>');
text = text.replace(/\n/g, '<br>');
// ── Render markdown ──
let html;
if (typeof marked !== 'undefined' && typeof DOMPurify !== 'undefined') {
html = _formatMarked(text);
} else {
html = _formatBasic(text);
}
return text;
// ── Restore thinking blocks ──
for (const b of thinkingBlocks) {
const inner = esc(b.content).replace(/\n/g, '<br>');
const thinkHTML = App.settings.showThinking
? `<details class="thinking-block"><summary>💭 Thinking</summary><div class="thinking-content">${inner}</div></details>`
: '';
// marked may wrap placeholder in <p> tags
html = html.replace(new RegExp(`<p>\\s*THINK_PLACEHOLDER_${b.id}\\s*</p>`, 'g'), thinkHTML);
html = html.replace(`THINK_PLACEHOLDER_${b.id}`, thinkHTML);
}
return html;
}
/**
* Full markdown rendering via marked.js + DOMPurify
*/
function _formatMarked(text) {
const rendered = marked.parse(text, {
breaks: true,
gfm: true
});
// Sanitize but allow code structure
let html = DOMPurify.sanitize(rendered, {
ADD_TAGS: ['details', 'summary'],
ADD_ATTR: ['id', 'class', 'onclick']
});
// Inject copy buttons into <pre><code> blocks
html = html.replace(/<pre><code([^>]*)>([\s\S]*?)<\/code><\/pre>/g, (_, attrs, code) => {
const codeId = 'code-' + Math.random().toString(36).slice(2, 9);
return `<pre><code id="${codeId}"${attrs}>${code}</code><button class="copy-code-btn" onclick="navigator.clipboard.writeText(document.getElementById('${codeId}').textContent).then(()=>UI.toast('Copied','success'))">Copy</button></pre>`;
});
return html;
}
/**
* Regex fallback when marked/DOMPurify unavailable
*/
function _formatBasic(text) {
let html = esc(text);
// Code blocks with copy button
html = html.replace(/```(\w*)\n?([\s\S]*?)```/g, (_, lang, code) => {
const id = 'code-' + Math.random().toString(36).slice(2, 9);
return `<pre><code id="${id}" class="language-${lang}">${code.trim()}</code><button class="copy-code-btn" onclick="navigator.clipboard.writeText(document.getElementById('${id}').textContent).then(()=>UI.toast('Copied','success'))">Copy</button></pre>`;
});
html = html.replace(/`([^`]+)`/g, '<code>$1</code>');
html = html.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
html = html.replace(/\*([^*]+)\*/g, '<em>$1</em>');
html = html.replace(/\n/g, '<br>');
return html;
}
function esc(s) {