Changeset 0.23.0 (#153)
This commit is contained in:
@@ -44,9 +44,71 @@ function formatMessage(content) {
|
||||
html = html.replace(`THINK_PLACEHOLDER_${b.id}`, thinkHTML);
|
||||
}
|
||||
|
||||
// @mention highlighting (v0.23.0): style @Name references as pills
|
||||
html = _highlightMentions(html);
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight @mentions in rendered HTML as styled pills.
|
||||
* Matches @Name patterns against the channel model roster.
|
||||
* Only highlights inside text nodes (not inside code/pre/a tags).
|
||||
*/
|
||||
function _highlightMentions(html) {
|
||||
if (typeof ChannelModels === 'undefined') return html;
|
||||
const roster = ChannelModels.getRoster();
|
||||
if (!roster || roster.length < 2) return html;
|
||||
|
||||
// Build patterns from handles (primary) and display names (fallback)
|
||||
const patterns = [];
|
||||
const seen = new Set();
|
||||
|
||||
for (const m of roster) {
|
||||
// Handle pattern (preferred)
|
||||
if (m.handle) {
|
||||
const escaped = m.handle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
if (!seen.has(escaped)) {
|
||||
patterns.push(new RegExp('(@' + escaped + ')(?=[\\s.,!?;:)\\]}<]|$)', 'gi'));
|
||||
seen.add(escaped);
|
||||
}
|
||||
}
|
||||
// Display name pattern (hyphenated form)
|
||||
if (m.display_name) {
|
||||
const hyphenated = m.display_name.replace(/\s+/g, '-');
|
||||
const escaped = hyphenated.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
if (!seen.has(escaped.toLowerCase())) {
|
||||
const flexible = escaped.replace(/[-]+/g, '[\\s-]+');
|
||||
patterns.push(new RegExp('(@' + flexible + ')(?=[\\s.,!?;:)\\]}<]|$)', 'gi'));
|
||||
seen.add(escaped.toLowerCase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @all
|
||||
patterns.push(new RegExp('(@all)(?=[\\s.,!?;:)\\]}<]|$)', 'gi'));
|
||||
|
||||
// Don't highlight inside <code>, <pre>, <a> tags
|
||||
const parts = html.split(/(<[^>]+>)/);
|
||||
let inCode = false;
|
||||
for (let i = 0; i < parts.length; i++) {
|
||||
const part = parts[i];
|
||||
if (part.startsWith('<')) {
|
||||
const tag = part.toLowerCase();
|
||||
if (tag.startsWith('<code') || tag.startsWith('<pre') || tag.startsWith('<a ')) inCode = true;
|
||||
if (tag.startsWith('</code') || tag.startsWith('</pre') || tag.startsWith('</a')) inCode = false;
|
||||
continue;
|
||||
}
|
||||
if (inCode) continue;
|
||||
let text = part;
|
||||
for (const re of patterns) {
|
||||
text = text.replace(re, '<span class="mention-pill">$1</span>');
|
||||
}
|
||||
parts[i] = text;
|
||||
}
|
||||
return parts.join('');
|
||||
}
|
||||
|
||||
function _formatMarked(text) {
|
||||
// ── Unwrap outer ```markdown wrappers ────────────────────────
|
||||
// LLMs frequently wrap entire responses in ```markdown ... ``` which is
|
||||
|
||||
Reference in New Issue
Block a user