feat(chat): enhance chat functionality with web search and article integration

- Updated ChatMessage and ChatWindow components to support inline web search results and articles.
- Integrated new web search and RSS plugins into the chat system for real-time information retrieval.
- Enhanced useContentPanel to manage web search results alongside existing media types.
- Added ArticleOverlay component for displaying selected articles from search results.
- Improved UI elements and styles for better user interaction with web search features.

Made-with: Cursor
This commit is contained in:
Dorian
2026-03-02 21:29:50 +00:00
parent 49ec6c09b6
commit 2d056f9498
36 changed files with 2616 additions and 303 deletions
+37
View File
@@ -0,0 +1,37 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
const SAFE_URL_SCHEME = /^https?:\/\//i
export const useArticleOverlayStore = defineStore('articleOverlay', () => {
const isOpen = ref(false)
const url = ref<string | null>(null)
const title = ref('')
const content = ref<string | null>(null)
const imgSrc = ref<string | null>(null)
function open(articleUrl: string, articleTitle = '', articleContent?: string, articleImgSrc?: string) {
const trimmed = String(articleUrl ?? '').trim()
if (!SAFE_URL_SCHEME.test(trimmed)) return
try {
new URL(trimmed)
} catch {
return
}
url.value = trimmed
title.value = String(articleTitle ?? 'Article').slice(0, 200)
content.value = typeof articleContent === 'string' && articleContent.trim().length > 0 ? articleContent.trim() : null
imgSrc.value = typeof articleImgSrc === 'string' && articleImgSrc.trim().length > 0 ? articleImgSrc.trim() : null
isOpen.value = true
}
function close() {
isOpen.value = false
url.value = null
title.value = ''
content.value = null
imgSrc.value = null
}
return { isOpen, url, title, content, imgSrc, open, close }
})
+16 -1
View File
@@ -1,6 +1,6 @@
import { defineStore } from 'pinia'
import { ref, computed, watch } from 'vue'
import type { Message, Conversation } from '@aiui/core/types/message'
import type { Message, Conversation, WebSearchResult } from '@aiui/core/types/message'
const isDev = import.meta.env.DEV
let saveTimer: ReturnType<typeof setTimeout> | null = null
@@ -52,6 +52,8 @@ export const useChatStore = defineStore('chat', () => {
const savedSide = localStorage.getItem('aiui-panel-side') as 'left' | 'right' | null
const panelSide = ref<'left' | 'right'>(savedSide ?? 'right')
const webSearchEnabled = ref(localStorage.getItem('aiui-web-search') !== 'false')
// Load chats from server on startup — _loaded gate prevents the watcher
// from overwriting the file with empty data before the load completes
if (isDev) {
@@ -75,6 +77,10 @@ export const useChatStore = defineStore('chat', () => {
localStorage.setItem('aiui-panel-side', val)
})
watch(webSearchEnabled, (val) => {
localStorage.setItem('aiui-web-search', String(val))
})
watch(
[conversations, activeConversationId],
([conv, active]) => {
@@ -134,6 +140,13 @@ export const useChatStore = defineStore('chat', () => {
last.content += text
}
function setMessageWebResults(conversationId: string, messageId: string, results: WebSearchResult[]) {
const conv = conversations.value.get(conversationId)
if (!conv) return
const msg = conv.messages.find((m) => m.id === messageId)
if (msg) msg.webResults = results
}
function switchSide() {
panelSide.value = panelSide.value === 'right' ? 'left' : 'right'
}
@@ -161,9 +174,11 @@ export const useChatStore = defineStore('chat', () => {
isStreaming,
loaded,
panelSide,
webSearchEnabled,
createConversation,
addMessage,
appendToLastMessage,
setMessageWebResults,
switchSide,
setActiveConversation,
deleteConversation,