feat: the assistant's answers in Markdown; thinking shows «Думаю…», then «Думал N с», opening smoothly by its arrow

This commit is contained in:
redtea committed 2026-10-16 09:33:23 +03:00
1 parent 4b7220fffb
commit 4da1cc4200
4 files changed
+103 -12

No files matched your search

+44 -1
View File
@@ -58,6 +58,49 @@
}
return out;
}
const api = {shellQuote, shellQuoteAll, dropAction, fmtSize, scope, parseActions};
// md turns an assistant's text (no ``` blocks - those are drawn apart)
// into HTML: headings, lists, quotes, bold, italic, `code` and http(s)
// links. Everything is escaped first, so nothing else gets through.
const escH = v => String(v).replace(/[&<>"]/g, c => ({'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;'}[c]));
function mdInline(t) {
const codes = [];
t = t.replace(/`([^`\n]+)`/g, (_, c) => { codes.push(c); return '\u0000' + (codes.length - 1) + '\u0000'; });
t = escH(t)
.replace(/\*\*([^*\n]+)\*\*/g, '<b>$1</b>')
.replace(/(^|[^*\w])\*([^*\n]+)\*(?!\w)/g, '$1<i>$2</i>')
.replace(/(^|\W)_([^_\n]+)_(?!\w)/g, '$1<i>$2</i>')
.replace(/\[([^\]\n]+)\]\(((?:[^()\s]|\([^()\s]*\))+)\)/g, (_, txt, url) => /^https?:\/\//i.test(url.replace(/&amp;/g, '&'))
? '<a href="' + url + '" target="_blank" rel="noopener">' + txt + '</a>' : txt);
return t.replace(/\u0000(\d+)\u0000/g, (_, i) => '<code>' + escH(codes[+i]) + '</code>');
}
function md(text) {
const out = [];
let para = [], list = null;
const flush = () => {
if (para.length) out.push('<p>' + para.map(mdInline).join('<br>') + '</p>');
para = [];
if (list) { out.push('<' + list.tag + '>' + list.items.map(x => '<li>' + mdInline(x) + '</li>').join('') + '</' + list.tag + '>'); list = null; }
};
for (const line of String(text).split('\n')) {
let m;
if (!line.trim()) { flush(); continue; }
if ((m = /^#{1,6}\s+(.*)$/.exec(line))) { flush(); out.push('<h4>' + mdInline(m[1]) + '</h4>'); continue; }
if (/^\s*(-{3,}|\*{3,})\s*$/.test(line)) { flush(); out.push('<hr>'); continue; }
if ((m = /^>\s?(.*)$/.exec(line))) { flush(); out.push('<blockquote>' + mdInline(m[1]) + '</blockquote>'); continue; }
const ul = /^\s*[-*•]\s+(.*)$/.exec(line), ol = /^\s*\d+[.)]\s+(.*)$/.exec(line);
if (ul || ol) {
const tag = ul ? 'ul' : 'ol';
if (para.length || (list && list.tag !== tag)) flush();
if (!list) list = {tag, items: []};
list.items.push((ul || ol)[1]);
continue;
}
if (list) flush();
para.push(line);
}
flush();
return out.join('');
}
const api = {shellQuote, shellQuoteAll, dropAction, fmtSize, scope, parseActions, md};
if (typeof module !== 'undefined') module.exports = api; else root.WSUtil = api;
})(this);