From 36a4e58017aa2a8e2320fcf36f06778aba1dc971 Mon Sep 17 00:00:00 2001 From: Andrew Zambazos Date: Fri, 12 Sep 2025 19:16:34 +1200 Subject: [PATCH] tried to add formatting to nebot page --- plugins/nebot/page.js | 48 +++++++++++++++++++++++++++---- plugins/nebot/renderer-preload.js | 28 +++++++++++++++++- 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/plugins/nebot/page.js b/plugins/nebot/page.js index 97be0cb..dea32cd 100644 --- a/plugins/nebot/page.js +++ b/plugins/nebot/page.js @@ -196,14 +196,24 @@ if(!chat){ return; } chat.messages.forEach(m=>{ const div = h('div',{class:'msg '+m.role}); - div.innerHTML = '
'+renderMarkdown(m.content)+'
'; - - // Enhance links for security + 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 (in case already rendered) div.querySelectorAll('a[href]').forEach(a => { a.setAttribute('target', '_blank'); a.setAttribute('rel', 'noopener noreferrer'); }); - + els.messages.appendChild(div); }); els.messages.scrollTop = els.messages.scrollHeight; @@ -308,6 +318,8 @@ typeNext(); } + // Keep a registry of handlers so we can remove previous listeners reliably + const streamHandlers = new Map(); function subscribeStream(id){ const channel = 'ollama-chat:stream:' + id; console.log('[Nebot Page] Subscribing to stream channel:', channel); @@ -316,9 +328,12 @@ typingQueue = []; isTyping = false; - // Remove any existing listeners for this channel + // Remove any existing listener registered earlier for this channel 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) { @@ -398,6 +413,7 @@ if (window.electronAPI && window.electronAPI.on) { console.log('[Nebot Page] Setting up stream listener via electronAPI'); window.electronAPI.on(channel, handleStreamPayload); + streamHandlers.set(channel, handleStreamPayload); } else { console.warn('[Nebot Page] electronAPI.on not available for stream subscription'); } @@ -583,4 +599,24 @@ initializeSettings().then(() => { 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); } })(); diff --git a/plugins/nebot/renderer-preload.js b/plugins/nebot/renderer-preload.js index 0fc336e..a652fd2 100644 --- a/plugins/nebot/renderer-preload.js +++ b/plugins/nebot/renderer-preload.js @@ -7,7 +7,10 @@ try { marked = require('marked'); hljs = require('highlight.js'); 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({ breaks: true, highlight(code, lang) { @@ -24,15 +27,38 @@ try { // Expose to page context so page.html no longer needs CDN scripts try { 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.DOMPurify = DOMPurify; window.hljs = hljs; } } 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) { // 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'; // Expose minimal API for page scripts (optional)