fix(chat): stop loading bubble early, no tab switch on inject, convert md->html in richtext
This commit is contained in:
@@ -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', {
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user