fix(chat): stop loading bubble early, no tab switch on inject, convert md->html in richtext

This commit is contained in:
2026-05-03 00:55:02 +02:00
parent ad3af531b8
commit afa8043fd5
2 changed files with 38 additions and 5 deletions

View File

@@ -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', {

View File

@@ -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, '<h6>$1</h6>')
.replace(/^#{5}\s(.+)$/gm, '<h5>$1</h5>')
.replace(/^#{4}\s(.+)$/gm, '<h4>$1</h4>')
.replace(/^#{3}\s(.+)$/gm, '<h3>$1</h3>')
.replace(/^#{2}\s(.+)$/gm, '<h2>$1</h2>')
.replace(/^#\s(.+)$/gm, '<h1>$1</h1>')
.replace(/\*\*\*(.+?)\*\*\*/g, '<strong><em>$1</em></strong>')
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
.replace(/\*(.+?)\*/g, '<em>$1</em>')
.replace(/__(.+?)__/g, '<strong>$1</strong>')
.replace(/_(.+?)_/g, '<em>$1</em>')
.replace(/`([^`]+)`/g, '<code>$1</code>')
.replace(/~~(.+?)~~/g, '<s>$1</s>')
.replace(/^[-*]\s(.+)$/gm, '<li>$1</li>')
.replace(/^\d+\.\s(.+)$/gm, '<li>$1</li>')
.replace(/^>\s(.+)$/gm, '<blockquote>$1</blockquote>')
.replace(/\n{2,}/g, '</p><p>')
.replace(/^(?!<[hH1-6]|<blockquote|<li|<pre|<p)(.+)$/gm, '<p>$1</p>')
.replace(/<p><\/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"