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 { 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(() => {
|
useEffect(() => {
|
||||||
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
|
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
|
||||||
@@ -304,16 +306,15 @@ export function ContextualAIChat({
|
|||||||
/** Called from chat hover-actions: inject a chat message into the note */
|
/** Called from chat hover-actions: inject a chat message into the note */
|
||||||
const handleInjectFromChat = async (msgText: string, mode: 'replace' | 'complete' | 'merge') => {
|
const handleInjectFromChat = async (msgText: string, mode: 'replace' | 'complete' | 'merge') => {
|
||||||
if (mode === 'replace') {
|
if (mode === 'replace') {
|
||||||
|
// Stay on chat tab — show preview inline via resourcePreview without switching tabs
|
||||||
setResourceText(msgText)
|
setResourceText(msgText)
|
||||||
setResourceMode('replace')
|
setResourceMode('replace')
|
||||||
setResourcePreview({ text: msgText, source: 'chat' })
|
setResourcePreview({ text: msgText, source: 'chat' })
|
||||||
setActiveTab('resource')
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setResourceText(msgText)
|
setResourceText(msgText)
|
||||||
setResourceMode(mode)
|
setResourceMode(mode)
|
||||||
setActiveTab('resource')
|
// Do NOT switch tabs — enrich and show preview in current tab
|
||||||
// Auto-launch enrichment
|
|
||||||
setResourceEnriching(true)
|
setResourceEnriching(true)
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/ai/enrich-from-resource', {
|
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 { useSearchParams } from 'next/navigation'
|
||||||
import { useLanguage } from '@/lib/i18n'
|
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 {
|
interface HistoryState {
|
||||||
title: string
|
title: string
|
||||||
content: string
|
content: string
|
||||||
@@ -1044,7 +1068,15 @@ export function NoteInput({
|
|||||||
noteTitle={title}
|
noteTitle={title}
|
||||||
noteContent={content}
|
noteContent={content}
|
||||||
noteImages={allImages}
|
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}
|
lastActionApplied={false}
|
||||||
notebooks={notebooks.map(nb => ({ id: nb.id, name: nb.name }))}
|
notebooks={notebooks.map(nb => ({ id: nb.id, name: nb.name }))}
|
||||||
className="border border-border border-l-0 rounded-r-xl overflow-hidden shadow-sm"
|
className="border border-border border-l-0 rounded-r-xl overflow-hidden shadow-sm"
|
||||||
|
|||||||
Reference in New Issue
Block a user