From b3dc603b944cbfe073e7e2bf5cd126017367d7ae Mon Sep 17 00:00:00 2001
From: Tyler <68524461+TySP-Dev@users.noreply.github.com>
Date: Sun, 17 May 2026 16:02:31 -0400
Subject: [PATCH] Better markdown support
---
ollama_answers.py | 95 ++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 85 insertions(+), 10 deletions(-)
diff --git a/ollama_answers.py b/ollama_answers.py
index e62a1bc..a8b48a6 100644
--- a/ollama_answers.py
+++ b/ollama_answers.py
@@ -408,6 +408,22 @@ INTERACTIVE_CSS = '''
padding-left: 0.5rem;
color: var(--color-base-font, #cdd6f4);
}
+ .sxng-md-content {
+ line-height: 1.6;
+ }
+ .sxng-md-content ul, .sxng-md-content ol {
+ margin: 0.5rem 0;
+ padding-left: 1.5rem;
+ }
+ .sxng-md-content li {
+ margin: 0.2rem 0;
+ }
+ .sxng-md-content p {
+ margin: 0.4rem 0;
+ }
+ .sxng-md-content code {
+ font-family: monospace;
+ }
'''
INTERACTIVE_HTML = '''
@@ -430,6 +446,62 @@ INTERACTIVE_HTML = '''
'''
CITATION_HELPER_JS = r'''
+ function parseMarkdown(text) {
+ text = text.replace(/\*\*(.*?)\*\*/g, '$1');
+ text = text.replace(/__(.*?)__/g, '$1');
+ text = text.replace(/(?$1');
+ text = text.replace(/(?$1');
+ text = text.replace(/`([^`]+)`/g, '$1');
+ text = text.replace(/((?:^|\n)[*\-+] .+)+/g, (match) => {
+ const items = match.trim().split('\n').map(line => {
+ const content = line.replace(/^[*\-+] /, '').trim();
+ return `
');
+ text = text.replace(/\n(?!<)/g, '
');
+ return text;
+ }
+
+ function linkCitationsInElement(el, urls) {
+ const walker = document.createTreeWalker(
+ el, NodeFilter.SHOW_TEXT, null
+ );
+ const textNodes = [];
+ let node;
+ while (node = walker.nextNode()) {
+ textNodes.push(node);
+ }
+ textNodes.forEach(textNode => {
+ const text = textNode.textContent;
+ if (!/\[\d/.test(text)) return;
+ const span = document.createElement('span');
+ span.innerHTML = text.replace(/\[(\d{1,2}(?:,\s*\d{1,2})*)\]/g, (match, nums) => {
+ return nums.split(/\s*,\s*/).map(n => {
+ const idx = parseInt(n.trim());
+ const url = urls[idx - 1];
+ if (url) {
+ return `[${n.trim()}]`;
+ }
+ return match;
+ }).join('');
+ });
+ textNode.parentNode.replaceChild(span, textNode);
+ });
+ }
+
function renderCitations(text, urls) {
const fragment = document.createDocumentFragment();
const re = /\[(\d{1,2}(?:\s*,\s*\d{1,2})*)\]/g;
@@ -1039,17 +1111,19 @@ FRONTEND_JS_TEMPLATE = r"""
if (cursor) cursor.remove();
- let last = data.lastChild;
- while (last) {
- if (last.textContent && last.textContent.trim().length === 0) {
- const prev = last.previousSibling;
- last.remove();
- last = prev;
- } else {
- if (last.textContent) last.textContent = last.textContent.trimEnd();
- break;
+ // Replace streamed text nodes with markdown-rendered content
+ Array.from(data.childNodes).forEach(node => {
+ if (node.nodeType !== 1 || !node.classList.contains('sxng-prior-history')) {
+ node.remove();
}
- }
+ });
+
+ const rendered = parseMarkdown(fullText.trim());
+ const mdDiv = document.createElement('div');
+ mdDiv.className = 'sxng-md-content';
+ mdDiv.innerHTML = rendered;
+ linkCitationsInElement(mdDiv, urls);
+ data.appendChild(mdDiv);
renderCitationFooter(fullText, urls, data);
@@ -1546,6 +1620,7 @@ class SXNGPlugin(Plugin):
"Answer the question directly using the provided context.",
"MUST CITE SOURCES by tailing a sentence with [n] or [n,n] etc. If citing general knowledge, use [*].",
"Never explain your process. The user expects a direct response.",
+ "Use markdown formatting where it improves clarity: **bold** for key terms, bullet lists for enumerations, numbered lists for steps. Keep formatting minimal and purposeful.",
intent_cfg['format'],
"If sources and general knowledge are insufficient, respond with 'Insufficient information to answer.'"
]