tried to add formatting to nebot page
This commit is contained in:
+40
-4
@@ -196,9 +196,19 @@
|
|||||||
if(!chat){ return; }
|
if(!chat){ return; }
|
||||||
chat.messages.forEach(m=>{
|
chat.messages.forEach(m=>{
|
||||||
const div = h('div',{class:'msg '+m.role});
|
const div = h('div',{class:'msg '+m.role});
|
||||||
div.innerHTML = '<div class="markdown">'+renderMarkdown(m.content)+'</div>';
|
const mdEl = h('div', { class: 'markdown' });
|
||||||
|
// If libs are ready, render now; otherwise, show plain text and mark for deferred upgrade
|
||||||
|
if (window.marked && window.DOMPurify) {
|
||||||
|
mdEl.innerHTML = renderMarkdown(m.content);
|
||||||
|
} else {
|
||||||
|
mdEl.textContent = m.content || '';
|
||||||
|
mdEl.dataset.raw = m.content || '';
|
||||||
|
deferredMarkdown.add(mdEl);
|
||||||
|
scheduleDeferredMarkdownCheck();
|
||||||
|
}
|
||||||
|
div.appendChild(mdEl);
|
||||||
|
|
||||||
// Enhance links for security
|
// Enhance links for security (in case already rendered)
|
||||||
div.querySelectorAll('a[href]').forEach(a => {
|
div.querySelectorAll('a[href]').forEach(a => {
|
||||||
a.setAttribute('target', '_blank');
|
a.setAttribute('target', '_blank');
|
||||||
a.setAttribute('rel', 'noopener noreferrer');
|
a.setAttribute('rel', 'noopener noreferrer');
|
||||||
@@ -308,6 +318,8 @@
|
|||||||
typeNext();
|
typeNext();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep a registry of handlers so we can remove previous listeners reliably
|
||||||
|
const streamHandlers = new Map();
|
||||||
function subscribeStream(id){
|
function subscribeStream(id){
|
||||||
const channel = 'ollama-chat:stream:' + id;
|
const channel = 'ollama-chat:stream:' + id;
|
||||||
console.log('[Nebot Page] Subscribing to stream channel:', channel);
|
console.log('[Nebot Page] Subscribing to stream channel:', channel);
|
||||||
@@ -316,9 +328,12 @@
|
|||||||
typingQueue = [];
|
typingQueue = [];
|
||||||
isTyping = false;
|
isTyping = false;
|
||||||
|
|
||||||
// Remove any existing listeners for this channel
|
// Remove any existing listener registered earlier for this channel
|
||||||
if (window.electronAPI && window.electronAPI.removeListener) {
|
if (window.electronAPI && window.electronAPI.removeListener) {
|
||||||
window.electronAPI.removeListener(channel, handleStreamPayload);
|
const prev = streamHandlers.get(channel);
|
||||||
|
if (prev) {
|
||||||
|
try { window.electronAPI.removeListener(channel, prev); } catch {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleStreamPayload(...args) {
|
function handleStreamPayload(...args) {
|
||||||
@@ -398,6 +413,7 @@
|
|||||||
if (window.electronAPI && window.electronAPI.on) {
|
if (window.electronAPI && window.electronAPI.on) {
|
||||||
console.log('[Nebot Page] Setting up stream listener via electronAPI');
|
console.log('[Nebot Page] Setting up stream listener via electronAPI');
|
||||||
window.electronAPI.on(channel, handleStreamPayload);
|
window.electronAPI.on(channel, handleStreamPayload);
|
||||||
|
streamHandlers.set(channel, handleStreamPayload);
|
||||||
} else {
|
} else {
|
||||||
console.warn('[Nebot Page] electronAPI.on not available for stream subscription');
|
console.warn('[Nebot Page] electronAPI.on not available for stream subscription');
|
||||||
}
|
}
|
||||||
@@ -583,4 +599,24 @@
|
|||||||
initializeSettings().then(() => {
|
initializeSettings().then(() => {
|
||||||
refreshList().then(()=>{ if(state.chats[0]) openChat(state.chats[0].id); });
|
refreshList().then(()=>{ if(state.chats[0]) openChat(state.chats[0].id); });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Listen for title updates from main (auto-generated titles)
|
||||||
|
try {
|
||||||
|
if (window.electronAPI && typeof window.electronAPI.on === 'function') {
|
||||||
|
window.electronAPI.on('ollama-chat:chat-updated', (payload) => {
|
||||||
|
const data = payload || {};
|
||||||
|
const { id, title } = data;
|
||||||
|
if (!id || !title) return;
|
||||||
|
// Update local state and rerender list
|
||||||
|
const item = state.chats.find(c => c.id === id);
|
||||||
|
if (item) {
|
||||||
|
item.title = title;
|
||||||
|
renderChatList();
|
||||||
|
} else {
|
||||||
|
// Fallback: refresh list from disk if we don't have it
|
||||||
|
refreshList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} catch (e) { console.warn('[Nebot Page] failed to attach chat-updated listener', e); }
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ try {
|
|||||||
marked = require('marked');
|
marked = require('marked');
|
||||||
hljs = require('highlight.js');
|
hljs = require('highlight.js');
|
||||||
createDOMPurify = require('dompurify');
|
createDOMPurify = require('dompurify');
|
||||||
DOMPurify = createDOMPurify(window);
|
// Defer DOMPurify creation until DOM is ready to avoid early failures in some contexts
|
||||||
|
try {
|
||||||
|
DOMPurify = createDOMPurify(window);
|
||||||
|
} catch {}
|
||||||
marked.setOptions({
|
marked.setOptions({
|
||||||
breaks: true,
|
breaks: true,
|
||||||
highlight(code, lang) {
|
highlight(code, lang) {
|
||||||
@@ -24,15 +27,38 @@ try {
|
|||||||
// Expose to page context so page.html no longer needs CDN scripts
|
// Expose to page context so page.html no longer needs CDN scripts
|
||||||
try {
|
try {
|
||||||
if (typeof window !== 'undefined') {
|
if (typeof window !== 'undefined') {
|
||||||
|
// Note: with contextIsolation enabled, assigning to window does not expose to main world.
|
||||||
|
// Keep assignments for same-world consumers, but also expose explicitly via contextBridge below.
|
||||||
window.marked = marked;
|
window.marked = marked;
|
||||||
window.DOMPurify = DOMPurify;
|
window.DOMPurify = DOMPurify;
|
||||||
window.hljs = hljs;
|
window.hljs = hljs;
|
||||||
}
|
}
|
||||||
} catch {}
|
} catch {}
|
||||||
|
// Explicitly expose to main world so internal pages (browser://nebot) can use these libs
|
||||||
|
try {
|
||||||
|
if (marked) contextBridge.exposeInMainWorld('marked', marked);
|
||||||
|
if (hljs) contextBridge.exposeInMainWorld('hljs', hljs);
|
||||||
|
if (DOMPurify) contextBridge.exposeInMainWorld('DOMPurify', DOMPurify);
|
||||||
|
} catch {}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// If libs aren't available yet, we'll gracefully render as plain text.
|
// If libs aren't available yet, we'll gracefully render as plain text.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If DOMPurify wasn't ready, create and expose it after DOM is ready
|
||||||
|
try {
|
||||||
|
window.addEventListener('DOMContentLoaded', () => {
|
||||||
|
try {
|
||||||
|
if (!DOMPurify && createDOMPurify) {
|
||||||
|
DOMPurify = createDOMPurify(window);
|
||||||
|
}
|
||||||
|
if (DOMPurify) {
|
||||||
|
try { contextBridge.exposeInMainWorld('DOMPurify', DOMPurify); } catch {}
|
||||||
|
try { window.DOMPurify = DOMPurify; } catch {}
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
});
|
||||||
|
} catch {}
|
||||||
|
|
||||||
const pluginId = 'ollama-chat';
|
const pluginId = 'ollama-chat';
|
||||||
|
|
||||||
// Expose minimal API for page scripts (optional)
|
// Expose minimal API for page scripts (optional)
|
||||||
|
|||||||
Reference in New Issue
Block a user