From afa8043fd5ffcb21d35cca03831866263cb8016f Mon Sep 17 00:00:00 2001
From: sepehr
Date: Sun, 3 May 2026 00:55:02 +0200
Subject: [PATCH] fix(chat): stop loading bubble early, no tab switch on
inject, convert md->html in richtext
---
.../components/contextual-ai-chat.tsx | 9 ++---
memento-note/components/note-input.tsx | 34 ++++++++++++++++++-
2 files changed, 38 insertions(+), 5 deletions(-)
diff --git a/memento-note/components/contextual-ai-chat.tsx b/memento-note/components/contextual-ai-chat.tsx
index 5a7a87a..85ca171 100644
--- a/memento-note/components/contextual-ai-chat.tsx
+++ b/memento-note/components/contextual-ai-chat.tsx
@@ -151,7 +151,9 @@ export function ContextualAIChat({
const { messages, sendMessage, status, stop } = useChat({ transport })
- const isLoading = status === 'submitted' || status === 'streaming'
+ const lastMsg = messages[messages.length - 1]
+ const lastMsgHasContent = lastMsg?.role === 'assistant' && !!getMessageContent(lastMsg)
+ const isLoading = (status === 'submitted' || status === 'streaming') && !lastMsgHasContent
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
@@ -304,16 +306,15 @@ export function ContextualAIChat({
/** Called from chat hover-actions: inject a chat message into the note */
const handleInjectFromChat = async (msgText: string, mode: 'replace' | 'complete' | 'merge') => {
if (mode === 'replace') {
+ // Stay on chat tab — show preview inline via resourcePreview without switching tabs
setResourceText(msgText)
setResourceMode('replace')
setResourcePreview({ text: msgText, source: 'chat' })
- setActiveTab('resource')
return
}
setResourceText(msgText)
setResourceMode(mode)
- setActiveTab('resource')
- // Auto-launch enrichment
+ // Do NOT switch tabs — enrich and show preview in current tab
setResourceEnriching(true)
try {
const res = await fetch('/api/ai/enrich-from-resource', {
diff --git a/memento-note/components/note-input.tsx b/memento-note/components/note-input.tsx
index 6841148..bae5d93 100644
--- a/memento-note/components/note-input.tsx
+++ b/memento-note/components/note-input.tsx
@@ -56,6 +56,30 @@ import { useSession } from 'next-auth/react'
import { useSearchParams } from 'next/navigation'
import { useLanguage } from '@/lib/i18n'
+/** Convert basic markdown to HTML for richtext injection from AI chat */
+function markdownToBasicHtml(md: string): string {
+ return md
+ .replace(/^#{6}\s(.+)$/gm, '$1
')
+ .replace(/^#{5}\s(.+)$/gm, '$1
')
+ .replace(/^#{4}\s(.+)$/gm, '$1
')
+ .replace(/^#{3}\s(.+)$/gm, '$1
')
+ .replace(/^#{2}\s(.+)$/gm, '$1
')
+ .replace(/^#\s(.+)$/gm, '$1
')
+ .replace(/\*\*\*(.+?)\*\*\*/g, '$1')
+ .replace(/\*\*(.+?)\*\*/g, '$1')
+ .replace(/\*(.+?)\*/g, '$1')
+ .replace(/__(.+?)__/g, '$1')
+ .replace(/_(.+?)_/g, '$1')
+ .replace(/`([^`]+)`/g, '$1')
+ .replace(/~~(.+?)~~/g, '$1')
+ .replace(/^[-*]\s(.+)$/gm, '$1')
+ .replace(/^\d+\.\s(.+)$/gm, '$1')
+ .replace(/^>\s(.+)$/gm, '$1
')
+ .replace(/\n{2,}/g, '
')
+ .replace(/^(?!<[hH1-6]|
$1')
+ .replace(/<\/p>/g, '')
+}
+
interface HistoryState {
title: string
content: string
@@ -1044,7 +1068,15 @@ export function NoteInput({
noteTitle={title}
noteContent={content}
noteImages={allImages}
- onApplyToNote={(newContent) => setContent(newContent)}
+ onApplyToNote={(newContent) => {
+ if (type === 'richtext') {
+ // If content looks like markdown, convert to HTML before injecting into richtext
+ const looksLikeMarkdown = /^#{1,6}\s|^[-*]\s|\*\*[^*]+\*\*|^>\s/.test(newContent)
+ setContent(looksLikeMarkdown ? markdownToBasicHtml(newContent) : newContent)
+ } else {
+ setContent(newContent)
+ }
+ }}
lastActionApplied={false}
notebooks={notebooks.map(nb => ({ id: nb.id, name: nb.name }))}
className="border border-border border-l-0 rounded-r-xl overflow-hidden shadow-sm"