diff --git a/electron/main/ai/agent.ts b/electron/main/ai/agent.ts index ac1cb4b..e3125f8 100644 --- a/electron/main/ai/agent.ts +++ b/electron/main/ai/agent.ts @@ -440,7 +440,8 @@ ${content.responseInstruction}` */ function getFallbackRoleDefinition(chatType: 'group' | 'private', locale: string = 'zh-CN'): string { const content = i18nContent[locale as keyof typeof i18nContent] || i18nContent['zh-CN'] - const chatTypeDesc = chatType === 'private' ? (locale === 'zh-CN' ? '私聊' : 'private chat') : (locale === 'zh-CN' ? '群聊' : 'group chat') + const chatTypeDesc = + chatType === 'private' ? (locale === 'zh-CN' ? '私聊' : 'private chat') : locale === 'zh-CN' ? '群聊' : 'group chat' return content.fallbackRoleDefinition(chatTypeDesc) } diff --git a/electron/main/ai/conversations.ts b/electron/main/ai/conversations.ts index 559deec..620f113 100644 --- a/electron/main/ai/conversations.ts +++ b/electron/main/ai/conversations.ts @@ -153,10 +153,12 @@ export function createConversation(sessionId: string, title?: string): AIConvers const now = Math.floor(Date.now() / 1000) const id = `conv_${Date.now()}_${Math.random().toString(36).slice(2, 8)}` - db.prepare(` + db.prepare( + ` INSERT INTO ai_conversation (id, session_id, title, created_at, updated_at) VALUES (?, ?, ?, ?, ?) - `).run(id, sessionId, title || null, now, now) + ` + ).run(id, sessionId, title || null, now, now) return { id, @@ -173,12 +175,16 @@ export function createConversation(sessionId: string, title?: string): AIConvers export function getConversations(sessionId: string): AIConversation[] { const db = getAiDb() - const rows = db.prepare(` + const rows = db + .prepare( + ` SELECT id, session_id as sessionId, title, created_at as createdAt, updated_at as updatedAt FROM ai_conversation WHERE session_id = ? ORDER BY updated_at DESC - `).all(sessionId) as AIConversation[] + ` + ) + .all(sessionId) as AIConversation[] return rows } @@ -189,11 +195,15 @@ export function getConversations(sessionId: string): AIConversation[] { export function getConversation(conversationId: string): AIConversation | null { const db = getAiDb() - const row = db.prepare(` + const row = db + .prepare( + ` SELECT id, session_id as sessionId, title, created_at as createdAt, updated_at as updatedAt FROM ai_conversation WHERE id = ? - `).get(conversationId) as AIConversation | undefined + ` + ) + .get(conversationId) as AIConversation | undefined return row || null } @@ -205,11 +215,15 @@ export function updateConversationTitle(conversationId: string, title: string): const db = getAiDb() const now = Math.floor(Date.now() / 1000) - const result = db.prepare(` + const result = db + .prepare( + ` UPDATE ai_conversation SET title = ?, updated_at = ? WHERE id = ? - `).run(title, now, conversationId) + ` + ) + .run(title, now, conversationId) return result.changes > 0 } @@ -245,10 +259,12 @@ export function addMessage( const now = Math.floor(Date.now() / 1000) const id = `msg_${Date.now()}_${Math.random().toString(36).slice(2, 8)}` - db.prepare(` + db.prepare( + ` INSERT INTO ai_message (id, conversation_id, role, content, timestamp, data_keywords, data_message_count, content_blocks) VALUES (?, ?, ?, ?, ?, ?, ?, ?) - `).run( + ` + ).run( id, conversationId, role, @@ -280,7 +296,9 @@ export function addMessage( export function getMessages(conversationId: string): AIMessage[] { const db = getAiDb() - const rows = db.prepare(` + const rows = db + .prepare( + ` SELECT id, conversation_id as conversationId, @@ -293,7 +311,9 @@ export function getMessages(conversationId: string): AIMessage[] { FROM ai_message WHERE conversation_id = ? ORDER BY timestamp ASC - `).all(conversationId) as Array<{ + ` + ) + .all(conversationId) as Array<{ id: string conversationId: string role: string diff --git a/electron/main/ai/llm/openai-compatible.ts b/electron/main/ai/llm/openai-compatible.ts index 2d13ff6..38420cd 100644 --- a/electron/main/ai/llm/openai-compatible.ts +++ b/electron/main/ai/llm/openai-compatible.ts @@ -49,10 +49,7 @@ function normalizeBaseUrl(baseUrl?: string): string { /** * MiniMax 流式返回可能是累计文本,这里按前缀增量去重 */ -function dedupeCumulativeStreamChunk( - chunk: string, - previousText: string -): { delta: string; nextText: string } { +function dedupeCumulativeStreamChunk(chunk: string, previousText: string): { delta: string; nextText: string } { if (!previousText) { return { delta: chunk, nextText: chunk } } @@ -70,7 +67,6 @@ function dedupeCumulativeStreamChunk( return { delta: chunk, nextText: previousText + chunk } } - /** * 包装 fetch:注入思考开关和 thought_signature */ diff --git a/electron/main/ai/llm/sdkUtils.ts b/electron/main/ai/llm/sdkUtils.ts index d600320..392835f 100644 --- a/electron/main/ai/llm/sdkUtils.ts +++ b/electron/main/ai/llm/sdkUtils.ts @@ -3,15 +3,7 @@ */ import { jsonSchema } from 'ai' -import type { - ContentPart, - FinishReason, - LanguageModelUsage, - ModelMessage, - ToolSet, - TypedToolCall, - JSONValue, -} from 'ai' +import type { ContentPart, FinishReason, LanguageModelUsage, ModelMessage, ToolSet, TypedToolCall, JSONValue } from 'ai' import type { ChatMessage, ChatResponse, ToolCall, ToolDefinition } from './types' const UNKNOWN_TOOL_NAME = 'unknown_tool' diff --git a/electron/main/ai/rag/chunking/index.ts b/electron/main/ai/rag/chunking/index.ts index 628fbf8..d6f7305 100644 --- a/electron/main/ai/rag/chunking/index.ts +++ b/electron/main/ai/rag/chunking/index.ts @@ -5,4 +5,3 @@ export { getSessionChunks, getSessionChunk, formatSessionChunk } from './session' export type { Chunk, ChunkMetadata, ChunkingOptions, SessionMessage, SessionInfo } from './types' export { INVALID_MESSAGE_TYPES, INVALID_TEXT_PATTERNS } from './types' - diff --git a/electron/main/ai/rag/chunking/session.ts b/electron/main/ai/rag/chunking/session.ts index 37dc52e..cb4a55d 100644 --- a/electron/main/ai/rag/chunking/session.ts +++ b/electron/main/ai/rag/chunking/session.ts @@ -177,10 +177,7 @@ function splitIntoSubChunks( /** * 从数据库获取会话列表 */ -function getSessionsFromDb( - db: Database.Database, - options: ChunkingOptions -): SessionInfo[] { +function getSessionsFromDb(db: Database.Database, options: ChunkingOptions): SessionInfo[] { const { limit = 50, timeFilter } = options let sql = ` @@ -213,11 +210,7 @@ function getSessionsFromDb( /** * 获取会话的消息 */ -function getSessionMessagesFromDb( - db: Database.Database, - sessionId: number, - limit: number = 500 -): SessionMessage[] { +function getSessionMessagesFromDb(db: Database.Database, sessionId: number, limit: number = 500): SessionMessage[] { const sql = ` SELECT m.id, @@ -387,4 +380,3 @@ export function getSessionChunk(dbPath: string, sessionId: number): Chunk | null } } } - diff --git a/electron/main/ai/rag/chunking/types.ts b/electron/main/ai/rag/chunking/types.ts index 8731b908..0a0a018 100644 --- a/electron/main/ai/rag/chunking/types.ts +++ b/electron/main/ai/rag/chunking/types.ts @@ -81,4 +81,3 @@ export interface ChunkingOptions { /** 单个切片最大字符数(超过会拆分为子切片) */ maxChunkChars?: number } - diff --git a/electron/main/ai/rag/config.ts b/electron/main/ai/rag/config.ts index b176c1f..baffc19 100644 --- a/electron/main/ai/rag/config.ts +++ b/electron/main/ai/rag/config.ts @@ -120,9 +120,7 @@ export function getEmbeddingConfigById(id: string): EmbeddingServiceConfig | nul /** * 添加新 Embedding 配置 */ -export function addEmbeddingConfig( - config: Omit -): { +export function addEmbeddingConfig(config: Omit): { success: boolean config?: EmbeddingServiceConfig error?: string diff --git a/electron/main/ai/rag/embedding/index.ts b/electron/main/ai/rag/embedding/index.ts index a331ec7..a0235f0 100644 --- a/electron/main/ai/rag/embedding/index.ts +++ b/electron/main/ai/rag/embedding/index.ts @@ -132,18 +132,19 @@ export async function validateEmbeddingConfig( ): Promise<{ success: boolean; error?: string }> { try { // 转换为 EmbeddingServiceConfig 格式 - const serviceConfig: EmbeddingServiceConfig = 'id' in config - ? config - : { - id: 'temp', - name: 'temp', - apiSource: config.apiSource || 'reuse_llm', - model: config.model || 'nomic-embed-text', - baseUrl: config.baseUrl, - apiKey: config.apiKey, - createdAt: Date.now(), - updatedAt: Date.now(), - } + const serviceConfig: EmbeddingServiceConfig = + 'id' in config + ? config + : { + id: 'temp', + name: 'temp', + apiSource: config.apiSource || 'reuse_llm', + model: config.model || 'nomic-embed-text', + baseUrl: config.baseUrl, + apiKey: config.apiKey, + createdAt: Date.now(), + updatedAt: Date.now(), + } const service = await createEmbeddingService(serviceConfig) const result = await service.validate() diff --git a/electron/main/ai/rag/embedding/openai-compatible.ts b/electron/main/ai/rag/embedding/openai-compatible.ts index 0c0738d..618969d 100644 --- a/electron/main/ai/rag/embedding/openai-compatible.ts +++ b/electron/main/ai/rag/embedding/openai-compatible.ts @@ -141,4 +141,3 @@ export class OpenAICompatibleEmbeddingService implements IEmbeddingService { // API 服务无需释放资源 } } - diff --git a/electron/main/ai/rag/index.ts b/electron/main/ai/rag/index.ts index 5434ee6..0dff3c3 100644 --- a/electron/main/ai/rag/index.ts +++ b/electron/main/ai/rag/index.ts @@ -64,13 +64,7 @@ export type { ChunkingOptions, SessionMessage, SessionInfo } from './chunking' // ==================== 向量存储 ==================== -export { - getVectorStore, - resetVectorStore, - getVectorStoreStats, - SQLiteVectorStore, - MemoryVectorStore, -} from './store' +export { getVectorStore, resetVectorStore, getVectorStoreStats, SQLiteVectorStore, MemoryVectorStore } from './store' // ==================== Pipeline ==================== diff --git a/electron/main/ai/rag/pipeline/index.ts b/electron/main/ai/rag/pipeline/index.ts index 2394311..bd44027 100644 --- a/electron/main/ai/rag/pipeline/index.ts +++ b/electron/main/ai/rag/pipeline/index.ts @@ -4,4 +4,3 @@ export { executeSemanticPipeline } from './semantic' export type { SemanticPipelineOptions, SemanticPipelineResult } from './types' - diff --git a/electron/main/ai/rag/pipeline/semantic.ts b/electron/main/ai/rag/pipeline/semantic.ts index 109c296..1bd58a7 100644 --- a/electron/main/ai/rag/pipeline/semantic.ts +++ b/electron/main/ai/rag/pipeline/semantic.ts @@ -84,11 +84,7 @@ function formatEvidenceBlock( return '' } - const lines = [ - ``, - `以下是与用户问题语义相关的历史对话片段(按相关度排序):`, - '', - ] + const lines = [``, `以下是与用户问题语义相关的历史对话片段(按相关度排序):`, ''] for (let i = 0; i < results.length; i++) { const result = results[i] @@ -108,9 +104,7 @@ function formatEvidenceBlock( /** * 执行 Semantic Pipeline */ -export async function executeSemanticPipeline( - options: SemanticPipelineOptions -): Promise { +export async function executeSemanticPipeline(options: SemanticPipelineOptions): Promise { const { userMessage, dbPath, timeFilter, abortSignal } = options // 获取 RAG 配置 @@ -230,10 +224,7 @@ export async function executeSemanticPipeline( const topResults = scoredResults.slice(0, topK) const topScore = topResults[0]?.score ?? 0 - logger.info( - 'RAG', - `✅ 语义搜索完成: 返回 ${topResults.length} 个结果,最高相关度 ${(topScore * 100).toFixed(1)}%` - ) + logger.info('RAG', `✅ 语义搜索完成: 返回 ${topResults.length} 个结果,最高相关度 ${(topScore * 100).toFixed(1)}%`) // 7. 生成证据块 const evidenceBlock = formatEvidenceBlock(rewrittenQuery, topResults) @@ -253,4 +244,3 @@ export async function executeSemanticPipeline( } } } - diff --git a/electron/main/ai/rag/pipeline/types.ts b/electron/main/ai/rag/pipeline/types.ts index efe8cde..dd3e0fe 100644 --- a/electron/main/ai/rag/pipeline/types.ts +++ b/electron/main/ai/rag/pipeline/types.ts @@ -4,4 +4,3 @@ */ export type { SemanticPipelineOptions, SemanticPipelineResult, ChunkMetadata } from '../types' - diff --git a/electron/main/ai/rag/store/index.ts b/electron/main/ai/rag/store/index.ts index 5cd7db1..15f0b03 100644 --- a/electron/main/ai/rag/store/index.ts +++ b/electron/main/ai/rag/store/index.ts @@ -109,4 +109,3 @@ export async function getVectorStoreStats(): Promise<{ export { SQLiteVectorStore } from './sqlite' export { MemoryVectorStore } from './memory' export type { IVectorStore, VectorSearchResult, VectorStoreStats, VectorStoreConfig } from './types' - diff --git a/electron/main/ai/rag/store/memory.ts b/electron/main/ai/rag/store/memory.ts index c0a54f4..0257fb4 100644 --- a/electron/main/ai/rag/store/memory.ts +++ b/electron/main/ai/rag/store/memory.ts @@ -149,9 +149,7 @@ export class MemoryVectorStore implements IVectorStore { /** * 批量添加向量 */ - async addBatch( - items: Array<{ id: string; vector: number[]; metadata?: Record }> - ): Promise { + async addBatch(items: Array<{ id: string; vector: number[]; metadata?: Record }>): Promise { for (const item of items) { await this.add(item.id, item.vector, item.metadata) } @@ -263,4 +261,3 @@ export class MemoryVectorStore implements IVectorStore { logger.info('[Memory Store] 已关闭') } } - diff --git a/electron/main/ai/rag/store/sqlite.ts b/electron/main/ai/rag/store/sqlite.ts index 89e293c..c51713b 100644 --- a/electron/main/ai/rag/store/sqlite.ts +++ b/electron/main/ai/rag/store/sqlite.ts @@ -101,9 +101,7 @@ export class SQLiteVectorStore implements IVectorStore { /** * 批量添加向量 */ - async addBatch( - items: Array<{ id: string; vector: number[]; metadata?: Record }> - ): Promise { + async addBatch(items: Array<{ id: string; vector: number[]; metadata?: Record }>): Promise { const insert = this.db.prepare(` INSERT OR REPLACE INTO vectors (id, vector, dimensions, metadata) VALUES (?, ?, ?, ?) @@ -123,9 +121,7 @@ export class SQLiteVectorStore implements IVectorStore { * 获取向量(BLOB → Float32Array) */ async get(id: string): Promise { - const row = this.db.prepare('SELECT vector FROM vectors WHERE id = ?').get(id) as - | { vector: Buffer } - | undefined + const row = this.db.prepare('SELECT vector FROM vectors WHERE id = ?').get(id) as { vector: Buffer } | undefined if (!row) return null @@ -194,9 +190,7 @@ export class SQLiteVectorStore implements IVectorStore { const countRow = this.db.prepare('SELECT COUNT(*) as count FROM vectors').get() as { count: number } // 获取第一个向量的维度 - const dimRow = this.db.prepare('SELECT dimensions FROM vectors LIMIT 1').get() as - | { dimensions: number } - | undefined + const dimRow = this.db.prepare('SELECT dimensions FROM vectors LIMIT 1').get() as { dimensions: number } | undefined // 获取数据库文件大小 let sizeBytes: number | undefined @@ -223,4 +217,3 @@ export class SQLiteVectorStore implements IVectorStore { logger.info('[SQLite Store] 已关闭') } } - diff --git a/electron/main/ai/rag/store/types.ts b/electron/main/ai/rag/store/types.ts index 0cb88d6..24ee7eb 100644 --- a/electron/main/ai/rag/store/types.ts +++ b/electron/main/ai/rag/store/types.ts @@ -4,4 +4,3 @@ */ export type { IVectorStore, VectorSearchResult, VectorStoreStats, VectorStoreConfig } from '../types' - diff --git a/electron/main/ai/rag/types.ts b/electron/main/ai/rag/types.ts index 394f716..70afd23 100644 --- a/electron/main/ai/rag/types.ts +++ b/electron/main/ai/rag/types.ts @@ -388,4 +388,3 @@ export interface SemanticPipelineResult { /** 错误信息 */ error?: string } - diff --git a/electron/main/ai/summary/index.ts b/electron/main/ai/summary/index.ts index 1363c15..c9b2f03 100644 --- a/electron/main/ai/summary/index.ts +++ b/electron/main/ai/summary/index.ts @@ -141,16 +141,23 @@ function isValidMessage(content: string): boolean { if (emojiOnlyPattern.test(trimmed)) return false // 过滤占位符文本 - const placeholders = ['[图片]', '[语音]', '[视频]', '[文件]', '[表情]', '[动画表情]', '[位置]', '[名片]', '[红包]', '[转账]', '[撤回消息]'] + const placeholders = [ + '[图片]', + '[语音]', + '[视频]', + '[文件]', + '[表情]', + '[动画表情]', + '[位置]', + '[名片]', + '[红包]', + '[转账]', + '[撤回消息]', + ] if (placeholders.some((p) => trimmed === p)) return false // 过滤系统消息(入群、退群等) - const systemPatterns = [ - /^.*邀请.*加入了群聊$/, - /^.*退出了群聊$/, - /^.*撤回了一条消息$/, - /^你撤回了一条消息$/, - ] + const systemPatterns = [/^.*邀请.*加入了群聊$/, /^.*退出了群聊$/, /^.*撤回了一条消息$/, /^你撤回了一条消息$/] if (systemPatterns.some((p) => p.test(trimmed))) return false return true @@ -494,4 +501,3 @@ export function checkSessionsCanGenerateSummary( return results } - diff --git a/electron/main/ai/tools/index.ts b/electron/main/ai/tools/index.ts index 473c9be..722f257 100644 --- a/electron/main/ai/tools/index.ts +++ b/electron/main/ai/tools/index.ts @@ -77,10 +77,7 @@ export async function getTool(name: string): Promise * @param toolCall LLM 返回的 tool_call * @param context 执行上下文 */ -export async function executeToolCall( - toolCall: ToolCall, - context: ToolContext -): Promise { +export async function executeToolCall(toolCall: ToolCall, context: ToolContext): Promise { await ensureToolsInitialized() const toolName = toolCall.function.name @@ -120,10 +117,7 @@ export async function executeToolCall( * @param toolCalls LLM 返回的 tool_calls 数组 * @param context 执行上下文 */ -export async function executeToolCalls( - toolCalls: ToolCall[], - context: ToolContext -): Promise { +export async function executeToolCalls(toolCalls: ToolCall[], context: ToolContext): Promise { // 并行执行所有工具调用 return Promise.all(toolCalls.map((tc) => executeToolCall(tc, context))) } @@ -143,4 +137,3 @@ export async function getRegisteredToolCount(): Promise { await ensureToolsInitialized() return toolRegistry.size } - diff --git a/electron/main/ai/tools/registry.ts b/electron/main/ai/tools/registry.ts index e21e798..98a8f87 100644 --- a/electron/main/ai/tools/registry.ts +++ b/electron/main/ai/tools/registry.ts @@ -1008,9 +1008,7 @@ async function getSessionSummariesExecutor( let filteredSessions = sessions if (params.keywords && params.keywords.length > 0) { const keywords = params.keywords.map((k) => k.toLowerCase()) - filteredSessions = sessions.filter((s) => - keywords.some((keyword) => s.summary?.toLowerCase().includes(keyword)) - ) + filteredSessions = sessions.filter((s) => keywords.some((keyword) => s.summary?.toLowerCase().includes(keyword))) } // 只返回有摘要的 diff --git a/electron/main/ai/tools/types.ts b/electron/main/ai/tools/types.ts index fc15d9a..f407e54 100644 --- a/electron/main/ai/tools/types.ts +++ b/electron/main/ai/tools/types.ts @@ -39,10 +39,7 @@ export interface ToolContext { * @param context 执行上下文 * @returns 执行结果(将被序列化为字符串传回 LLM) */ -export type ToolExecutor> = ( - params: T, - context: ToolContext -) => Promise +export type ToolExecutor> = (params: T, context: ToolContext) => Promise /** * 注册的工具 @@ -73,4 +70,3 @@ export interface ToolExecutionResult { /** 错误信息(失败时) */ error?: string } - diff --git a/electron/main/ipc/ai.ts b/electron/main/ipc/ai.ts index 7431c91..9ab5cac 100644 --- a/electron/main/ipc/ai.ts +++ b/electron/main/ipc/ai.ts @@ -161,9 +161,7 @@ export function registerAIHandlers({ win }: IpcContext): void { return { success: false, error: '暂无 AI 日志文件' } } - const logFiles = fs - .readdirSync(logDir) - .filter((name) => name.startsWith('ai_') && name.endsWith('.log')) + const logFiles = fs.readdirSync(logDir).filter((name) => name.startsWith('ai_') && name.endsWith('.log')) if (logFiles.length === 0) { return { success: false, error: '暂无 AI 日志文件' } @@ -734,11 +732,7 @@ export function registerAIHandlers({ win }: IpcContext): void { */ ipcMain.handle( 'embedding:updateConfig', - async ( - _, - id: string, - updates: Partial> - ) => { + async (_, id: string, updates: Partial>) => { try { aiLogger.info('IPC', '更新 Embedding 配置', { id }) const result = rag.updateEmbeddingConfig(id, updates) diff --git a/electron/main/ipc/cache.ts b/electron/main/ipc/cache.ts index bf72d90..7066131 100644 --- a/electron/main/ipc/cache.ts +++ b/electron/main/ipc/cache.ts @@ -4,14 +4,7 @@ import * as fs from 'fs/promises' import * as fsSync from 'fs' import * as path from 'path' import type { IpcContext } from './types' -import { - getAppDataDir, - getDatabaseDir, - getAiDataDir, - getLogsDir, - getDownloadsDir, - ensureDir, -} from '../paths' +import { getAppDataDir, getDatabaseDir, getAiDataDir, getLogsDir, getDownloadsDir, ensureDir } from '../paths' /** * 递归计算目录大小 diff --git a/electron/main/ipc/chat.ts b/electron/main/ipc/chat.ts index 80a9435..0542a52 100644 --- a/electron/main/ipc/chat.ts +++ b/electron/main/ipc/chat.ts @@ -777,7 +777,12 @@ export function registerChatHandlers(ctx: IpcContext): void { console.log('[IPC] session:generateSummary 收到请求:', { dbSessionId, chatSessionId, locale, forceRegenerate }) try { const { generateSessionSummary } = await import('../ai/summary') - const result = await generateSessionSummary(dbSessionId, chatSessionId, locale || 'zh-CN', forceRegenerate || false) + const result = await generateSessionSummary( + dbSessionId, + chatSessionId, + locale || 'zh-CN', + forceRegenerate || false + ) console.log('[IPC] session:generateSummary 返回:', result) return result } catch (error) { @@ -806,56 +811,53 @@ export function registerChatHandlers(ctx: IpcContext): void { /** * 批量检查会话是否可以生成摘要 */ - ipcMain.handle( - 'session:checkCanGenerateSummary', - async (_, dbSessionId: string, chatSessionIds: number[]) => { - try { - const { checkSessionsCanGenerateSummary } = await import('../ai/summary') - const results = checkSessionsCanGenerateSummary(dbSessionId, chatSessionIds) - // 将 Map 转换为普通对象以便 IPC 传输 - const obj: Record = {} - for (const [id, result] of results) { - obj[id] = result - } - return obj - } catch (error) { - console.error('批量检查会话摘要失败:', error) - return {} + ipcMain.handle('session:checkCanGenerateSummary', async (_, dbSessionId: string, chatSessionIds: number[]) => { + try { + const { checkSessionsCanGenerateSummary } = await import('../ai/summary') + const results = checkSessionsCanGenerateSummary(dbSessionId, chatSessionIds) + // 将 Map 转换为普通对象以便 IPC 传输 + const obj: Record = {} + for (const [id, result] of results) { + obj[id] = result } + return obj + } catch (error) { + console.error('批量检查会话摘要失败:', error) + return {} } - ) + }) /** * 根据时间范围查询会话列表 */ - ipcMain.handle( - 'session:getByTimeRange', - async (_, dbSessionId: string, startTs: number, endTs: number) => { - console.log('[session:getByTimeRange] 查询参数:', { dbSessionId, startTs, endTs }) - console.log('[session:getByTimeRange] 时间范围:', { - start: new Date(startTs * 1000).toISOString(), - end: new Date(endTs * 1000).toISOString(), + ipcMain.handle('session:getByTimeRange', async (_, dbSessionId: string, startTs: number, endTs: number) => { + console.log('[session:getByTimeRange] 查询参数:', { dbSessionId, startTs, endTs }) + console.log('[session:getByTimeRange] 时间范围:', { + start: new Date(startTs * 1000).toISOString(), + end: new Date(endTs * 1000).toISOString(), + }) + + try { + const { openDatabase } = await import('../database/core') + const db = openDatabase(dbSessionId, true) + if (!db) { + console.log('[session:getByTimeRange] 数据库打开失败') + return [] + } + + // 先查询总数和时间范围 + const stats = db + .prepare('SELECT COUNT(*) as count, MIN(start_ts) as minTs, MAX(start_ts) as maxTs FROM chat_session') + .get() as { count: number; minTs: number; maxTs: number } + console.log('[session:getByTimeRange] 数据库会话统计:', { + count: stats.count, + minTs: stats.minTs ? new Date(stats.minTs * 1000).toISOString() : null, + maxTs: stats.maxTs ? new Date(stats.maxTs * 1000).toISOString() : null, }) - try { - const { openDatabase } = await import('../database/core') - const db = openDatabase(dbSessionId, true) - if (!db) { - console.log('[session:getByTimeRange] 数据库打开失败') - return [] - } - - // 先查询总数和时间范围 - const stats = db.prepare('SELECT COUNT(*) as count, MIN(start_ts) as minTs, MAX(start_ts) as maxTs FROM chat_session').get() as { count: number; minTs: number; maxTs: number } - console.log('[session:getByTimeRange] 数据库会话统计:', { - count: stats.count, - minTs: stats.minTs ? new Date(stats.minTs * 1000).toISOString() : null, - maxTs: stats.maxTs ? new Date(stats.maxTs * 1000).toISOString() : null, - }) - - const sessions = db - .prepare( - ` + const sessions = db + .prepare( + ` SELECT id, start_ts as startTs, @@ -866,23 +868,22 @@ export function registerChatHandlers(ctx: IpcContext): void { WHERE start_ts >= ? AND start_ts <= ? ORDER BY start_ts DESC ` - ) - .all(startTs, endTs) as Array<{ - id: number - startTs: number - endTs: number - messageCount: number - summary: string | null - }> + ) + .all(startTs, endTs) as Array<{ + id: number + startTs: number + endTs: number + messageCount: number + summary: string | null + }> - console.log('[session:getByTimeRange] 查询结果数量:', sessions.length) - return sessions - } catch (error) { - console.error('查询时间范围会话失败:', error) - return [] - } + console.log('[session:getByTimeRange] 查询结果数量:', sessions.length) + return sessions + } catch (error) { + console.error('查询时间范围会话失败:', error) + return [] } - ) + }) /** * 获取最近 N 条会话 diff --git a/electron/main/ipc/network.ts b/electron/main/ipc/network.ts index f589931..4482480 100644 --- a/electron/main/ipc/network.ts +++ b/electron/main/ipc/network.ts @@ -29,26 +29,23 @@ export function registerNetworkHandlers(_context: IpcContext): void { /** * 保存代理配置 */ - ipcMain.handle( - 'network:saveProxyConfig', - (_event, config: ProxyConfig): { success: boolean; error?: string } => { - try { - // 如果是手动模式且填写了 URL,验证 URL 格式 - if (config.mode === 'manual' && config.url) { - const validation = validateProxyUrl(config.url) - if (!validation.valid) { - return { success: false, error: validation.error } - } + ipcMain.handle('network:saveProxyConfig', (_event, config: ProxyConfig): { success: boolean; error?: string } => { + try { + // 如果是手动模式且填写了 URL,验证 URL 格式 + if (config.mode === 'manual' && config.url) { + const validation = validateProxyUrl(config.url) + if (!validation.valid) { + return { success: false, error: validation.error } } - - saveProxyConfig(config) - return { success: true } - } catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error) - return { success: false, error: `保存配置失败: ${errorMessage}` } } + + saveProxyConfig(config) + return { success: true } + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + return { success: false, error: `保存配置失败: ${errorMessage}` } } - ) + }) /** * 测试代理连接 @@ -62,4 +59,3 @@ export function registerNetworkHandlers(_context: IpcContext): void { console.log('[IpcMain] Network handlers registered') } - diff --git a/electron/main/ipc/nlp.ts b/electron/main/ipc/nlp.ts index 69e1345..9719b1c 100644 --- a/electron/main/ipc/nlp.ts +++ b/electron/main/ipc/nlp.ts @@ -16,26 +16,20 @@ export function registerNlpHandlers(_ctx: IpcContext): void { * 获取词频统计 * 用于词云展示 */ - ipcMain.handle( - 'nlp:getWordFrequency', - async ( - _event, - params: WordFrequencyParams - ): Promise => { - try { - const result = await worker.query('getWordFrequency', params) - return result as WordFrequencyResult - } catch (error) { - console.error('[NLP] 获取词频统计失败:', error) - return { - words: [], - totalWords: 0, - totalMessages: 0, - uniqueWords: 0, - } + ipcMain.handle('nlp:getWordFrequency', async (_event, params: WordFrequencyParams): Promise => { + try { + const result = await worker.query('getWordFrequency', params) + return result as WordFrequencyResult + } catch (error) { + console.error('[NLP] 获取词频统计失败:', error) + return { + words: [], + totalWords: 0, + totalMessages: 0, + uniqueWords: 0, } } - ) + }) /** * 单文本分词 @@ -43,12 +37,7 @@ export function registerNlpHandlers(_ctx: IpcContext): void { */ ipcMain.handle( 'nlp:segmentText', - async ( - _event, - text: string, - locale: SupportedLocale, - minLength?: number - ): Promise => { + async (_event, text: string, locale: SupportedLocale, minLength?: number): Promise => { try { const result = await worker.query('segmentText', { text, locale, minLength }) return result as string[] diff --git a/electron/main/merger/index.ts b/electron/main/merger/index.ts index c26279a..3c5efee 100644 --- a/electron/main/merger/index.ts +++ b/electron/main/merger/index.ts @@ -632,9 +632,7 @@ export async function exportSessionToTempFile(sessionId: string): Promise t.meaningful).map((t) => t.tag) -) +export const MEANINGFUL_POS_TAGS = new Set(POS_TAG_DEFINITIONS.filter((t) => t.meaningful).map((t) => t.tag)) /** * 获取所有词性标签信息 @@ -92,7 +90,8 @@ export function getPosTagDefinitions(): PosTagInfo[] { } // 用于过滤的正则表达式 -const EMOJI_REGEX = /[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E0}-\u{1F1FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/gu +const EMOJI_REGEX = + /[\u{1F600}-\u{1F64F}\u{1F300}-\u{1F5FF}\u{1F680}-\u{1F6FF}\u{1F1E0}-\u{1F1FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/gu const PUNCTUATION_REGEX = /[!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~,。!?、;:""''()【】《》…—~·\s]/g const URL_REGEX = /https?:\/\/[^\s]+/g const MENTION_REGEX = /@[^\s@]+/g @@ -115,7 +114,12 @@ function cleanText(text: string): string { /** * 判断是否为有效词语 */ -function isValidWord(word: string, locale: SupportedLocale, minLength: number, enableStopwords: boolean = true): boolean { +function isValidWord( + word: string, + locale: SupportedLocale, + minLength: number, + enableStopwords: boolean = true +): boolean { // 空字符串 if (!word || word.trim().length === 0) return false @@ -206,9 +210,7 @@ function segmentChinese(text: string, options: ChineseSegmentOptions = {}): stri allowedTags = MEANINGFUL_POS_TAGS } - return tagged - .filter((item) => allowedTags.has(item.tag)) - .map((item) => item.word) + return tagged.filter((item) => allowedTags.has(item.tag)).map((item) => item.word) } catch (error) { console.error('[NLP] 中文分词失败:', error) // 降级:使用简单分词 @@ -232,9 +234,7 @@ function segmentEnglish(text: string): string[] { const segmenter = new Intl.Segmenter('en', { granularity: 'word' }) const segments = segmenter.segment(cleaned) - return [...segments] - .filter((segment) => segment.isWordLike) - .map((segment) => segment.segment.toLowerCase()) + return [...segments].filter((segment) => segment.isWordLike).map((segment) => segment.segment.toLowerCase()) } catch { // 降级:简单按空格分词 return cleaned @@ -265,17 +265,8 @@ export interface SegmentOptions { * @param options 分词选项 * @returns 过滤后的分词结果 */ -export function segment( - text: string, - locale: SupportedLocale, - options: SegmentOptions = {} -): string[] { - const { - minLength, - posFilterMode = 'meaningful', - customPosTags, - enableStopwords = true, - } = options +export function segment(text: string, locale: SupportedLocale, options: SegmentOptions = {}): string[] { + const { minLength, posFilterMode = 'meaningful', customPosTags, enableStopwords = true } = options const defaultMinLength = locale === 'zh-CN' ? 2 : 3 const effectiveMinLength = minLength ?? defaultMinLength @@ -330,9 +321,7 @@ export function batchSegmentWithFrequency( } // 排序并取 topN - const sorted = [...filtered.entries()] - .sort((a, b) => b[1] - a[1]) - .slice(0, topN) + const sorted = [...filtered.entries()].sort((a, b) => b[1] - a[1]).slice(0, topN) return new Map(sorted) } diff --git a/electron/main/nlp/stopwords.ts b/electron/main/nlp/stopwords.ts index 008c38e..6f2dba7 100644 --- a/electron/main/nlp/stopwords.ts +++ b/electron/main/nlp/stopwords.ts @@ -6,91 +6,544 @@ /** 中文停用词 */ export const CHINESE_STOPWORDS = new Set([ // 代词 - '我', '你', '他', '她', '它', '我们', '你们', '他们', '她们', '它们', - '自己', '别人', '大家', '谁', '什么', '哪', '哪里', '哪儿', '这', '那', - '这个', '那个', '这些', '那些', '这里', '那里', '这儿', '那儿', '这样', '那样', + '我', + '你', + '他', + '她', + '它', + '我们', + '你们', + '他们', + '她们', + '它们', + '自己', + '别人', + '大家', + '谁', + '什么', + '哪', + '哪里', + '哪儿', + '这', + '那', + '这个', + '那个', + '这些', + '那些', + '这里', + '那里', + '这儿', + '那儿', + '这样', + '那样', // 助词 - '的', '地', '得', '了', '着', '过', '吗', '呢', '吧', '啊', - '呀', '哇', '哦', '嗯', '噢', '喔', '呃', '唉', '哎', '嘛', + '的', + '地', + '得', + '了', + '着', + '过', + '吗', + '呢', + '吧', + '啊', + '呀', + '哇', + '哦', + '嗯', + '噢', + '喔', + '呃', + '唉', + '哎', + '嘛', // 介词 - '在', '从', '到', '向', '往', '把', '被', '给', '跟', '和', - '与', '对', '比', '为', '因', '由', '以', '按', '用', '让', + '在', + '从', + '到', + '向', + '往', + '把', + '被', + '给', + '跟', + '和', + '与', + '对', + '比', + '为', + '因', + '由', + '以', + '按', + '用', + '让', // 连词 - '和', '与', '或', '或者', '而', '并', '并且', '但', '但是', '可是', - '然而', '不过', '只是', '如果', '要是', '假如', '虽然', '尽管', '即使', '所以', - '因此', '于是', '那么', '因为', '由于', '既然', '为了', '以便', + '和', + '与', + '或', + '或者', + '而', + '并', + '并且', + '但', + '但是', + '可是', + '然而', + '不过', + '只是', + '如果', + '要是', + '假如', + '虽然', + '尽管', + '即使', + '所以', + '因此', + '于是', + '那么', + '因为', + '由于', + '既然', + '为了', + '以便', // 副词 - '不', '没', '没有', '很', '太', '最', '更', '也', '都', '就', - '才', '又', '再', '还', '却', '只', '只是', '已', '已经', '曾', - '曾经', '正', '正在', '将', '将要', '会', '能', '可以', '可能', '应该', - '必须', '一定', '大概', '也许', '或许', '其实', '确实', '真的', '当然', '一直', - '总是', '经常', '常常', '往往', '偶尔', '几乎', '差不多', '简直', '反正', '终于', + '不', + '没', + '没有', + '很', + '太', + '最', + '更', + '也', + '都', + '就', + '才', + '又', + '再', + '还', + '却', + '只', + '只是', + '已', + '已经', + '曾', + '曾经', + '正', + '正在', + '将', + '将要', + '会', + '能', + '可以', + '可能', + '应该', + '必须', + '一定', + '大概', + '也许', + '或许', + '其实', + '确实', + '真的', + '当然', + '一直', + '总是', + '经常', + '常常', + '往往', + '偶尔', + '几乎', + '差不多', + '简直', + '反正', + '终于', // 量词 - '个', '只', '条', '件', '位', '种', '些', '点', '下', '次', + '个', + '只', + '条', + '件', + '位', + '种', + '些', + '点', + '下', + '次', // 数词 - '一', '二', '三', '四', '五', '六', '七', '八', '九', '十', - '百', '千', '万', '亿', '两', '几', '多', '少', '第', '每', + '一', + '二', + '三', + '四', + '五', + '六', + '七', + '八', + '九', + '十', + '百', + '千', + '万', + '亿', + '两', + '几', + '多', + '少', + '第', + '每', // 动词(常见无实意动词) - '是', '有', '在', '做', '去', '来', '说', '看', '想', '要', - '能', '会', '让', '给', '叫', '用', '打', '把', '被', '到', + '是', + '有', + '在', + '做', + '去', + '来', + '说', + '看', + '想', + '要', + '能', + '会', + '让', + '给', + '叫', + '用', + '打', + '把', + '被', + '到', // 其他常见词 - '上', '下', '前', '后', '里', '外', '中', '内', '左', '右', - '东', '南', '西', '北', '时', '时候', '现在', '今天', '明天', '昨天', - '年', '月', '日', '号', '点', '分', '秒', '周', '星期', + '上', + '下', + '前', + '后', + '里', + '外', + '中', + '内', + '左', + '右', + '东', + '南', + '西', + '北', + '时', + '时候', + '现在', + '今天', + '明天', + '昨天', + '年', + '月', + '日', + '号', + '点', + '分', + '秒', + '周', + '星期', // 网络聊天常见无意义词 - '好', '好的', '行', '可以', '嗯嗯', '哈', '呵', '额', '恩', '昂', - 'ok', 'OK', '好吧', '知道', '知道了', '谢谢', '感谢', '抱歉', '不好意思', + '好', + '好的', + '行', + '可以', + '嗯嗯', + '哈', + '呵', + '额', + '恩', + '昂', + 'ok', + 'OK', + '好吧', + '知道', + '知道了', + '谢谢', + '感谢', + '抱歉', + '不好意思', // 语气词和程度词(虽然词性是名词/动词,但在聊天中无实际意义) - '感觉', '有点', '可能', '应该', '好像', '觉得', '认为', '看看', '看到', - '说', '问', '找', '弄', '搞', '搞定', '整', '干', '做', '来', '去', - '有', '没有', '没', '是不是', '有没有', '能不能', '会不会', '要不要', - '怎样', '如何', '为何', '为什么', '怎么', '怎么样', '怎么办', - '东西', '事情', '事', '问题', '时候', '地方', '情况', '样子', '意思', - '一下', '一点', '一些', '一样', '一起', '一直', '一般', '一定', '差不多', + '感觉', + '有点', + '可能', + '应该', + '好像', + '觉得', + '认为', + '看看', + '看到', + '说', + '问', + '找', + '弄', + '搞', + '搞定', + '整', + '干', + '做', + '来', + '去', + '有', + '没有', + '没', + '是不是', + '有没有', + '能不能', + '会不会', + '要不要', + '怎样', + '如何', + '为何', + '为什么', + '怎么', + '怎么样', + '怎么办', + '东西', + '事情', + '事', + '问题', + '时候', + '地方', + '情况', + '样子', + '意思', + '一下', + '一点', + '一些', + '一样', + '一起', + '一直', + '一般', + '一定', + '差不多', ]) /** 英文停用词 */ export const ENGLISH_STOPWORDS = new Set([ // Articles - 'a', 'an', 'the', + 'a', + 'an', + 'the', // Pronouns - 'i', 'me', 'my', 'myself', 'we', 'our', 'ours', 'ourselves', - 'you', 'your', 'yours', 'yourself', 'yourselves', - 'he', 'him', 'his', 'himself', 'she', 'her', 'hers', 'herself', - 'it', 'its', 'itself', 'they', 'them', 'their', 'theirs', 'themselves', - 'what', 'which', 'who', 'whom', 'this', 'that', 'these', 'those', + 'i', + 'me', + 'my', + 'myself', + 'we', + 'our', + 'ours', + 'ourselves', + 'you', + 'your', + 'yours', + 'yourself', + 'yourselves', + 'he', + 'him', + 'his', + 'himself', + 'she', + 'her', + 'hers', + 'herself', + 'it', + 'its', + 'itself', + 'they', + 'them', + 'their', + 'theirs', + 'themselves', + 'what', + 'which', + 'who', + 'whom', + 'this', + 'that', + 'these', + 'those', // Prepositions - 'in', 'on', 'at', 'by', 'for', 'with', 'about', 'against', 'between', - 'into', 'through', 'during', 'before', 'after', 'above', 'below', 'to', - 'from', 'up', 'down', 'out', 'off', 'over', 'under', 'again', 'further', + 'in', + 'on', + 'at', + 'by', + 'for', + 'with', + 'about', + 'against', + 'between', + 'into', + 'through', + 'during', + 'before', + 'after', + 'above', + 'below', + 'to', + 'from', + 'up', + 'down', + 'out', + 'off', + 'over', + 'under', + 'again', + 'further', // Conjunctions - 'and', 'but', 'or', 'nor', 'so', 'yet', 'both', 'either', 'neither', - 'not', 'only', 'own', 'same', 'than', 'too', 'very', 'just', + 'and', + 'but', + 'or', + 'nor', + 'so', + 'yet', + 'both', + 'either', + 'neither', + 'not', + 'only', + 'own', + 'same', + 'than', + 'too', + 'very', + 'just', // Be verbs - 'am', 'is', 'are', 'was', 'were', 'be', 'been', 'being', + 'am', + 'is', + 'are', + 'was', + 'were', + 'be', + 'been', + 'being', // Have verbs - 'have', 'has', 'had', 'having', + 'have', + 'has', + 'had', + 'having', // Do verbs - 'do', 'does', 'did', 'doing', + 'do', + 'does', + 'did', + 'doing', // Modal verbs - 'will', 'would', 'shall', 'should', 'can', 'could', 'may', 'might', 'must', + 'will', + 'would', + 'shall', + 'should', + 'can', + 'could', + 'may', + 'might', + 'must', // Other common words - 'if', 'then', 'else', 'when', 'where', 'why', 'how', 'all', 'each', - 'every', 'both', 'few', 'more', 'most', 'other', 'some', 'such', 'no', - 'any', 'now', 'here', 'there', 'of', 'as', + 'if', + 'then', + 'else', + 'when', + 'where', + 'why', + 'how', + 'all', + 'each', + 'every', + 'both', + 'few', + 'more', + 'most', + 'other', + 'some', + 'such', + 'no', + 'any', + 'now', + 'here', + 'there', + 'of', + 'as', // Contractions (without apostrophe) - 'dont', 'doesnt', 'didnt', 'wont', 'wouldnt', 'cant', 'couldnt', - 'shouldnt', 'isnt', 'arent', 'wasnt', 'werent', 'havent', 'hasnt', 'hadnt', + 'dont', + 'doesnt', + 'didnt', + 'wont', + 'wouldnt', + 'cant', + 'couldnt', + 'shouldnt', + 'isnt', + 'arent', + 'wasnt', + 'werent', + 'havent', + 'hasnt', + 'hadnt', // Chat common words - 'ok', 'okay', 'yes', 'no', 'yeah', 'yep', 'nope', 'sure', 'thanks', - 'thank', 'please', 'sorry', 'hi', 'hello', 'hey', 'bye', 'goodbye', - 'well', 'like', 'know', 'think', 'want', 'need', 'get', 'got', 'go', - 'going', 'come', 'coming', 'make', 'made', 'take', 'took', 'see', 'saw', - 'look', 'looking', 'say', 'said', 'tell', 'told', 'ask', 'asked', - 'let', 'put', 'keep', 'give', 'gave', 'find', 'found', 'try', 'tried', + 'ok', + 'okay', + 'yes', + 'no', + 'yeah', + 'yep', + 'nope', + 'sure', + 'thanks', + 'thank', + 'please', + 'sorry', + 'hi', + 'hello', + 'hey', + 'bye', + 'goodbye', + 'well', + 'like', + 'know', + 'think', + 'want', + 'need', + 'get', + 'got', + 'go', + 'going', + 'come', + 'coming', + 'make', + 'made', + 'take', + 'took', + 'see', + 'saw', + 'look', + 'looking', + 'say', + 'said', + 'tell', + 'told', + 'ask', + 'asked', + 'let', + 'put', + 'keep', + 'give', + 'gave', + 'find', + 'found', + 'try', + 'tried', // Time words - 'today', 'tomorrow', 'yesterday', 'now', 'then', 'always', 'never', - 'sometimes', 'often', 'usually', 'still', 'already', 'soon', 'later', + 'today', + 'tomorrow', + 'yesterday', + 'now', + 'then', + 'always', + 'never', + 'sometimes', + 'often', + 'usually', + 'still', + 'already', + 'soon', + 'later', ]) /** diff --git a/electron/main/parser/formats/chatlab-jsonl.ts b/electron/main/parser/formats/chatlab-jsonl.ts index ebb7c91..0564637 100644 --- a/electron/main/parser/formats/chatlab-jsonl.ts +++ b/electron/main/parser/formats/chatlab-jsonl.ts @@ -293,4 +293,3 @@ const module_: FormatModule = { } export default module_ - diff --git a/electron/main/parser/formats/instagram-native.ts b/electron/main/parser/formats/instagram-native.ts index 8270a84..ea9f11f 100644 --- a/electron/main/parser/formats/instagram-native.ts +++ b/electron/main/parser/formats/instagram-native.ts @@ -361,4 +361,3 @@ const module_: FormatModule = { } export default module_ - diff --git a/electron/main/parser/formats/line-native-txt.ts b/electron/main/parser/formats/line-native-txt.ts index a02e0c7..b4fff60 100644 --- a/electron/main/parser/formats/line-native-txt.ts +++ b/electron/main/parser/formats/line-native-txt.ts @@ -275,7 +275,7 @@ async function* parseLINE(options: ParseOptions): AsyncGenerator { // 检测文件格式 const formatFeature = detectFormat(filePath) @@ -55,7 +55,7 @@ export async function analyzeIncrementalImport( SELECT ts, m.platform_id as sender_platform_id, content FROM message msg JOIN member m ON msg.sender_id = m.id - `, + ` ) .all() as Array<{ ts: number; sender_platform_id: string; content: string | null }> @@ -102,7 +102,7 @@ export async function analyzeIncrementalImport( export async function incrementalImport( sessionId: string, filePath: string, - requestId: string, + requestId: string ): Promise { // 检测文件格式 const formatFeature = detectFormat(filePath) @@ -129,7 +129,7 @@ export async function incrementalImport( SELECT ts, m.platform_id as sender_platform_id, content FROM message msg JOIN member m ON msg.sender_id = m.id - `, + ` ) .all() as Array<{ ts: number; sender_platform_id: string; content: string | null }> @@ -176,7 +176,7 @@ export async function incrementalImport( m.platformId, m.accountName || null, m.groupNickname || null, - m.avatar || null, + m.avatar || null ) memberIdMap.set(m.platformId, result.lastInsertRowid as number) } @@ -202,7 +202,7 @@ export async function incrementalImport( msg.senderPlatformId, msg.senderAccountName || null, msg.senderGroupNickname || null, - null, + null ) memberId = result.lastInsertRowid as number memberIdMap.set(msg.senderPlatformId, memberId) @@ -215,7 +215,7 @@ export async function incrementalImport( msg.senderGroupNickname || null, msg.timestamp, msg.type, - msg.content || null, + msg.content || null ) // 添加到已有 key 集合(防止文件内重复) @@ -262,4 +262,3 @@ export async function incrementalImport( return { success: false, newMessageCount: 0, error: String(error) } } } - diff --git a/electron/main/worker/import/streamImport.ts b/electron/main/worker/import/streamImport.ts index 71c1949..ae0b2a7 100644 --- a/electron/main/worker/import/streamImport.ts +++ b/electron/main/worker/import/streamImport.ts @@ -16,7 +16,16 @@ import { type ParsedMessage, } from '../../parser' import { getDbDir } from '../core' -import { initPerfLog, logPerf, logPerfDetail, resetPerfLog, logInfo, logError, logSummary, getCurrentLogFile } from '../core' +import { + initPerfLog, + logPerf, + logPerfDetail, + resetPerfLog, + logInfo, + logError, + logSummary, + getCurrentLogFile, +} from '../core' import { sendProgress, generateSessionId, getDbPath, createDatabaseWithoutIndexes, createIndexes } from './utils' /** 跳过消息的原因统计 */ @@ -641,7 +650,9 @@ export async function streamImport(filePath: string, requestId: string): Promise logInfo(`onLog 调用次数: ${callbackStats.onLogCalls}`) logInfo(`onMeta 调用次数: ${callbackStats.onMetaCalls}`) logInfo(`onMembers 调用次数: ${callbackStats.onMembersCalls}, 总成员数: ${callbackStats.totalMembersReceived}`) - logInfo(`onMessageBatch 调用次数: ${callbackStats.onMessageBatchCalls}, 总消息数: ${callbackStats.totalMessagesReceived}`) + logInfo( + `onMessageBatch 调用次数: ${callbackStats.onMessageBatchCalls}, 总消息数: ${callbackStats.totalMessagesReceived}` + ) if ( callbackStats.skippedNoSenderId > 0 || callbackStats.skippedNoAccountName > 0 || @@ -650,8 +661,10 @@ export async function streamImport(filePath: string, requestId: string): Promise ) { logInfo(`=== 消息跳过统计 ===`) if (callbackStats.skippedNoSenderId > 0) logInfo(` 无 senderPlatformId: ${callbackStats.skippedNoSenderId}`) - if (callbackStats.skippedNoAccountName > 0) logInfo(` 无 senderAccountName: ${callbackStats.skippedNoAccountName}`) - if (callbackStats.skippedInvalidTimestamp > 0) logInfo(` 无效 timestamp: ${callbackStats.skippedInvalidTimestamp}`) + if (callbackStats.skippedNoAccountName > 0) + logInfo(` 无 senderAccountName: ${callbackStats.skippedNoAccountName}`) + if (callbackStats.skippedInvalidTimestamp > 0) + logInfo(` 无效 timestamp: ${callbackStats.skippedInvalidTimestamp}`) if (callbackStats.skippedNoType > 0) logInfo(` 无 type: ${callbackStats.skippedNoType}`) } @@ -660,7 +673,9 @@ export async function streamImport(filePath: string, requestId: string): Promise // 检查消息数量,如果为 0 则视为导入失败 if (totalMessageCount === 0) { - logError(`导入失败:未解析到任何消息 (收到 ${callbackStats.totalMessagesReceived} 条消息,全部被跳过或未收到任何消息)`) + logError( + `导入失败:未解析到任何消息 (收到 ${callbackStats.totalMessagesReceived} 条消息,全部被跳过或未收到任何消息)` + ) // 标记需要删除数据库文件(将在 finally 中执行,确保数据库已关闭) shouldDeleteDb = true importError = 'error.no_messages' diff --git a/electron/main/worker/import/tempDb.ts b/electron/main/worker/import/tempDb.ts index b535997..728aed3 100644 --- a/electron/main/worker/import/tempDb.ts +++ b/electron/main/worker/import/tempDb.ts @@ -86,12 +86,7 @@ export function cleanupTempDatabase(dbPath: string): void { * 生成消息去重键 * 使用 timestamp + senderPlatformId + contentLength 作为去重标识 */ -export function generateMessageKey( - timestamp: number, - senderPlatformId: string, - content: string | null, -): string { +export function generateMessageKey(timestamp: number, senderPlatformId: string, content: string | null): string { const contentLength = content ? content.length : 0 return `${timestamp}_${senderPlatformId}_${contentLength}` } - diff --git a/electron/main/worker/import/utils.ts b/electron/main/worker/import/utils.ts index bc5c4d1..c75cb3f 100644 --- a/electron/main/worker/import/utils.ts +++ b/electron/main/worker/import/utils.ts @@ -133,4 +133,3 @@ export function createIndexes(db: Database.Database): void { CREATE INDEX IF NOT EXISTS idx_context_session ON message_context(session_id); `) } - diff --git a/electron/main/worker/query/advanced/activity.ts b/electron/main/worker/query/advanced/activity.ts index e25019a..1c3d307 100644 --- a/electron/main/worker/query/advanced/activity.ts +++ b/electron/main/worker/query/advanced/activity.ts @@ -613,4 +613,3 @@ export function getCheckInAnalysis(sessionId: string, filter?: TimeFilter): any totalDays, } } - diff --git a/electron/main/worker/query/advanced/behavior.ts b/electron/main/worker/query/advanced/behavior.ts index d35b310..9f4e038 100644 --- a/electron/main/worker/query/advanced/behavior.ts +++ b/electron/main/worker/query/advanced/behavior.ts @@ -350,4 +350,3 @@ export function getMemeBattleAnalysis(sessionId: string, filter?: TimeFilter): a totalBattles: battles.length, } } - diff --git a/electron/main/worker/query/advanced/index.ts b/electron/main/worker/query/advanced/index.ts index 9e83968..f343dce 100644 --- a/electron/main/worker/query/advanced/index.ts +++ b/electron/main/worker/query/advanced/index.ts @@ -15,4 +15,3 @@ export { getMonologueAnalysis, getMemeBattleAnalysis } from './behavior' // 社交分析:@ 互动、含笑量 export { getMentionAnalysis, getMentionGraph, getLaughAnalysis } from './social' export type { MentionGraphData, MentionGraphNode, MentionGraphLink } from './social' - diff --git a/electron/main/worker/query/advanced/repeat.ts b/electron/main/worker/query/advanced/repeat.ts index c891b43..7eab984 100644 --- a/electron/main/worker/query/advanced/repeat.ts +++ b/electron/main/worker/query/advanced/repeat.ts @@ -80,7 +80,10 @@ export function getRepeatAnalysis(sessionId: string, filter?: TimeFilter): any { const fastestRepeaterStats = new Map() - const processRepeatChain = (chain: Array<{ id: number; senderId: number; content: string; ts: number }>, breakerId?: number) => { + const processRepeatChain = ( + chain: Array<{ id: number; senderId: number; content: string; ts: number }>, + breakerId?: number + ) => { if (chain.length < 3) return totalRepeatChains++ @@ -112,7 +115,13 @@ export function getRepeatAnalysis(sessionId: string, filter?: TimeFilter): any { existing.firstMessageId = firstMsgId } } else { - contentStats.set(content, { count: 1, maxChainLength: chainLength, originatorId, lastTs: chainTs, firstMessageId: firstMsgId }) + contentStats.set(content, { + count: 1, + maxChainLength: chainLength, + originatorId, + lastTs: chainTs, + firstMessageId: firstMsgId, + }) } // 计算反应时间 (Fastest Follower) diff --git a/electron/main/worker/query/advanced/social.ts b/electron/main/worker/query/advanced/social.ts index 724b048..f36d359 100644 --- a/electron/main/worker/query/advanced/social.ts +++ b/electron/main/worker/query/advanced/social.ts @@ -368,9 +368,9 @@ export function getMentionGraph(sessionId: string, filter?: TimeFilter): Mention nameToMemberId.set(member.name, member.id) // 查询历史昵称 - const history = db - .prepare(`SELECT name FROM member_name_history WHERE member_id = ?`) - .all(member.id) as Array<{ name: string }> + const history = db.prepare(`SELECT name FROM member_name_history WHERE member_id = ?`).all(member.id) as Array<{ + name: string + }> for (const h of history) { if (!nameToMemberId.has(h.name)) { diff --git a/electron/main/worker/query/basic.ts b/electron/main/worker/query/basic.ts index 619cf78..bd95af7 100644 --- a/electron/main/worker/query/basic.ts +++ b/electron/main/worker/query/basic.ts @@ -308,7 +308,10 @@ export function getMessageTypeDistribution(sessionId: string, filter?: TimeFilte * - detail: 1-25 字逐字分布 * - grouped: 5-100+ 每5字一组 */ -export function getMessageLengthDistribution(sessionId: string, filter?: TimeFilter): { +export function getMessageLengthDistribution( + sessionId: string, + filter?: TimeFilter +): { detail: Array<{ len: number; count: number }> grouped: Array<{ range: string; count: number }> } { @@ -368,9 +371,7 @@ export function getMessageLengthDistribution(sessionId: string, filter?: TimeFil const grouped: Array<{ range: string; count: number }> = [] for (const r of ranges5) { - const count = rows - .filter((row) => row.len >= r.min && row.len <= r.max) - .reduce((sum, row) => sum + row.count, 0) + const count = rows.filter((row) => row.len >= r.min && row.len <= r.max).reduce((sum, row) => sum + row.count, 0) grouped.push({ range: r.label, count, @@ -436,11 +437,7 @@ interface DbMeta { * 获取私聊对方成员的头像 * 逻辑参考 private-chat/index.vue 的 otherMemberAvatar */ -function getPrivateChatMemberAvatar( - db: Database.Database, - sessionName: string, - ownerId: string | null -): string | null { +function getPrivateChatMemberAvatar(db: Database.Database, sessionName: string, ownerId: string | null): string | null { // 获取所有非系统消息成员(按消息数排序) const members = db .prepare( @@ -758,10 +755,7 @@ export interface MembersPaginatedResult { /** * 获取成员列表(分页版本,支持搜索和排序) */ -export function getMembersPaginated( - sessionId: string, - params: MembersPaginationParams -): MembersPaginatedResult { +export function getMembersPaginated(sessionId: string, params: MembersPaginationParams): MembersPaginatedResult { const { page = 1, pageSize = 20, search = '', sortOrder = 'desc' } = params // 先确保数据库有 aliases 和 avatar 字段(兼容旧数据库) diff --git a/electron/main/worker/query/nlp.ts b/electron/main/worker/query/nlp.ts index b28eb69..7ad9d76 100644 --- a/electron/main/worker/query/nlp.ts +++ b/electron/main/worker/query/nlp.ts @@ -45,9 +45,11 @@ export function getWordFrequency(params: WordFrequencyParams): WordFrequencyResu // 构建 WHERE 子句,排除系统消息 let whereClause = clause if (whereClause.includes('WHERE')) { - whereClause += " AND COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0 AND msg.content IS NOT NULL AND TRIM(msg.content) != ''" + whereClause += + " AND COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0 AND msg.content IS NOT NULL AND TRIM(msg.content) != ''" } else { - whereClause = " WHERE COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0 AND msg.content IS NOT NULL AND TRIM(msg.content) != ''" + whereClause = + " WHERE COALESCE(m.account_name, '') != '系统消息' AND msg.type = 0 AND msg.content IS NOT NULL AND TRIM(msg.content) != ''" } // 查询消息内容 @@ -78,11 +80,7 @@ export function getWordFrequency(params: WordFrequencyParams): WordFrequencyResu // 收集词性统计(用于显示每个词性有多少词,仅中文有效) let posTagStats: PosTagStat[] | undefined if ((locale as SupportedLocale) === 'zh-CN') { - const posStatsMap = collectPosTagStats( - texts, - minWordLength ?? 2, - enableStopwords - ) + const posStatsMap = collectPosTagStats(texts, minWordLength ?? 2, enableStopwords) posTagStats = [...posStatsMap.entries()].map(([tag, count]) => ({ tag, count })) } @@ -121,11 +119,7 @@ export function getWordFrequency(params: WordFrequencyParams): WordFrequencyResu /** * 单文本分词(用于调试或其他用途) */ -export function segmentText( - text: string, - locale: SupportedLocale, - minLength?: number -): string[] { +export function segmentText(text: string, locale: SupportedLocale, minLength?: number): string[] { return segment(text, locale, { minLength }) } diff --git a/electron/main/worker/query/sql.ts b/electron/main/worker/query/sql.ts index 94c6104..ef1a501 100644 --- a/electron/main/worker/query/sql.ts +++ b/electron/main/worker/query/sql.ts @@ -128,12 +128,9 @@ export function executeRawSQL(sessionId: string, sql: string): SQLResult { } catch (error) { if (error instanceof Error) { // 美化错误信息 - const message = error.message - .replace(/^SQLITE_ERROR: /, '') - .replace(/^SQLITE_READONLY: /, '只读模式:') + const message = error.message.replace(/^SQLITE_ERROR: /, '').replace(/^SQLITE_READONLY: /, '只读模式:') throw new Error(message) } throw error } } - diff --git a/electron/main/worker/workerManager.ts b/electron/main/worker/workerManager.ts index 26d78e6..2884823 100644 --- a/electron/main/worker/workerManager.ts +++ b/electron/main/worker/workerManager.ts @@ -208,10 +208,7 @@ export async function closeWorkerAsync(): Promise { console.log('[WorkerManager] Closing worker async...') try { // 等待关闭所有数据库连接(最多等待 3 秒) - await Promise.race([ - sendToWorker('closeAll', {}), - new Promise((resolve) => setTimeout(resolve, 3000)), - ]) + await Promise.race([sendToWorker('closeAll', {}), new Promise((resolve) => setTimeout(resolve, 3000))]) } catch { // 忽略错误,继续终止 } diff --git a/electron/preload/index.d.ts b/electron/preload/index.d.ts index 1bcfcd5..6bba89f 100644 --- a/electron/preload/index.d.ts +++ b/electron/preload/index.d.ts @@ -109,10 +109,7 @@ interface ChatApi { getDailyActivity: (sessionId: string, filter?: TimeFilter) => Promise getWeekdayActivity: (sessionId: string, filter?: TimeFilter) => Promise getMonthlyActivity: (sessionId: string, filter?: TimeFilter) => Promise - getYearlyActivity: ( - sessionId: string, - filter?: TimeFilter - ) => Promise> + getYearlyActivity: (sessionId: string, filter?: TimeFilter) => Promise> getMessageLengthDistribution: ( sessionId: string, filter?: TimeFilter diff --git a/src/components/analysis/AIChat/ChatMessage.vue b/src/components/analysis/AIChat/ChatMessage.vue index e43d077..103077e 100644 --- a/src/components/analysis/AIChat/ChatMessage.vue +++ b/src/components/analysis/AIChat/ChatMessage.vue @@ -334,10 +334,7 @@ function formatToolParams(tool: ToolBlockContent): string {
diff --git a/src/components/analysis/AIChat/ChatStatusBar.vue b/src/components/analysis/AIChat/ChatStatusBar.vue index 89a3d6e..1cc1286 100644 --- a/src/components/analysis/AIChat/ChatStatusBar.vue +++ b/src/components/analysis/AIChat/ChatStatusBar.vue @@ -115,111 +115,111 @@ async function openAiLogFile() {
- - - - - - - - +
diff --git a/src/components/analysis/Filter/FilterHistory.vue b/src/components/analysis/Filter/FilterHistory.vue index 693fce5..417a865 100644 --- a/src/components/analysis/Filter/FilterHistory.vue +++ b/src/components/analysis/Filter/FilterHistory.vue @@ -237,4 +237,3 @@ defineExpose({ - diff --git a/src/components/analysis/IncrementalImportModal.vue b/src/components/analysis/IncrementalImportModal.vue index 0b42b7a..68ebf1e 100644 --- a/src/components/analysis/IncrementalImportModal.vue +++ b/src/components/analysis/IncrementalImportModal.vue @@ -206,23 +206,18 @@ function translateError(error: string): string { {{ t('analysis.incremental.description', { name: sessionName }) }}

- + - diff --git a/src/components/analysis/Overview/OverviewStatCards.vue b/src/components/analysis/Overview/OverviewStatCards.vue index 4d0bafb..fefc60d 100644 --- a/src/components/analysis/Overview/OverviewStatCards.vue +++ b/src/components/analysis/Overview/OverviewStatCards.vue @@ -25,7 +25,12 @@ defineProps<{ - diff --git a/src/components/common/UserSelect.vue b/src/components/common/UserSelect.vue index 9f9252c..862b94c 100644 --- a/src/components/common/UserSelect.vue +++ b/src/components/common/UserSelect.vue @@ -35,9 +35,7 @@ const internalValue = computed({ // 成员选项 const memberOptions = computed(() => { - const options: { value: string; label: string }[] = [ - { value: ALL_MEMBERS_VALUE, label: t('allMembers') }, - ] + const options: { value: string; label: string }[] = [{ value: ALL_MEMBERS_VALUE, label: t('allMembers') }] members.value.forEach((m) => { const displayName = m.groupNickname || m.accountName || m.platformId options.push({ @@ -95,4 +93,3 @@ watch( } } - diff --git a/src/components/common/settings/AI/AIPromptEditModal.vue b/src/components/common/settings/AI/AIPromptEditModal.vue index 9f4ff3c..de1ae66 100644 --- a/src/components/common/settings/AI/AIPromptEditModal.vue +++ b/src/components/common/settings/AI/AIPromptEditModal.vue @@ -198,9 +198,9 @@ ${formData.value.responseRules}`
- +
@@ -232,9 +232,9 @@ ${formData.value.responseRules}`
- + - {{ preset.applicableTo === 'group' ? t('settings.aiPrompt.preset.groupOnly') : t('settings.aiPrompt.preset.privateOnly') }} + {{ + preset.applicableTo === 'group' + ? t('settings.aiPrompt.preset.groupOnly') + : t('settings.aiPrompt.preset.privateOnly') + }}
diff --git a/src/components/common/settings/AI/EmbeddingConfigEditModal.vue b/src/components/common/settings/AI/EmbeddingConfigEditModal.vue index 628c3f5..1d9d1c0 100644 --- a/src/components/common/settings/AI/EmbeddingConfigEditModal.vue +++ b/src/components/common/settings/AI/EmbeddingConfigEditModal.vue @@ -176,13 +176,7 @@ async function saveConfig() {

{{ modalTitle }}

- +
@@ -205,12 +199,7 @@ async function saveConfig() { - +

{{ t('settings.embedding.apiSourceHint') }}

@@ -222,11 +211,7 @@ async function saveConfig() { {{ t('settings.embedding.model') }} * - +

{{ t('settings.embedding.modelHint') }}

@@ -301,4 +286,3 @@ async function saveConfig() { - diff --git a/src/components/common/settings/AI/RAGConfigTab.vue b/src/components/common/settings/AI/RAGConfigTab.vue index 597a97c..423553a 100644 --- a/src/components/common/settings/AI/RAGConfigTab.vue +++ b/src/components/common/settings/AI/RAGConfigTab.vue @@ -94,9 +94,7 @@ onMounted(() => {

-
+
{{ t('settings.embedding.configList') }} @@ -148,11 +146,13 @@ onMounted(() => {
{{ config.model }} · - {{ - config.apiSource === 'reuse_llm' - ? t('settings.embedding.reuseLLM') - : t('settings.embedding.customAPI') - }} + + {{ + config.apiSource === 'reuse_llm' + ? t('settings.embedding.reuseLLM') + : t('settings.embedding.customAPI') + }} +
@@ -197,9 +197,7 @@ onMounted(() => {
-
+

diff --git a/src/components/common/settings/DataStorage/SessionIndexSection.vue b/src/components/common/settings/DataStorage/SessionIndexSection.vue index c636cbf..48a63ac 100644 --- a/src/components/common/settings/DataStorage/SessionIndexSection.vue +++ b/src/components/common/settings/DataStorage/SessionIndexSection.vue @@ -200,7 +200,9 @@ onMounted(() => {

-
+
{{ t('settings.storage.session.defaultThreshold') }} @@ -287,4 +289,3 @@ onMounted(() => {
- diff --git a/src/components/common/settings/DataStorage/StorageManageSection.vue b/src/components/common/settings/DataStorage/StorageManageSection.vue index b4e6cdf..3aae9f1 100644 --- a/src/components/common/settings/DataStorage/StorageManageSection.vue +++ b/src/components/common/settings/DataStorage/StorageManageSection.vue @@ -211,4 +211,3 @@ defineExpose({
- diff --git a/src/components/view/InteractionView.vue b/src/components/view/InteractionView.vue index 7684307..d468412 100644 --- a/src/components/view/InteractionView.vue +++ b/src/components/view/InteractionView.vue @@ -100,23 +100,17 @@ watch(
- -
- {{ t('directed') }}: - -
- - - {{ t('reset') }} - -
+ +
+ {{ t('directed') }}: +
+ + + {{ t('reset') }} + + +
@@ -136,7 +130,9 @@ watch( :height="'100%'" /> -
+
{{ t('graphHint', { nodes: graphData.nodes.length, links: graphData.links.length }) }}
@@ -175,4 +171,3 @@ watch( } } - diff --git a/src/components/view/MessageView.vue b/src/components/view/MessageView.vue index 75ca0de..2aa5c3d 100644 --- a/src/components/view/MessageView.vue +++ b/src/components/view/MessageView.vue @@ -304,16 +304,9 @@ watch(
-
+
-
+
{{ item.name }} @@ -335,9 +328,7 @@ watch( {{ item.count.toLocaleString() }} - - ({{ item.percentage }}%) - + ({{ item.percentage }}%)
@@ -378,11 +369,7 @@ watch(
- +
{{ t('noData') }}
diff --git a/src/components/view/index.ts b/src/components/view/index.ts index 34ed8cd..0d3f635 100644 --- a/src/components/view/index.ts +++ b/src/components/view/index.ts @@ -1,4 +1,3 @@ // 视图组件统一导出 export { default as MessageView } from './MessageView.vue' export { default as InteractionView } from './InteractionView.vue' - diff --git a/src/i18n/locales/en-US/index.ts b/src/i18n/locales/en-US/index.ts index 537c00e..5f23e47 100644 --- a/src/i18n/locales/en-US/index.ts +++ b/src/i18n/locales/en-US/index.ts @@ -15,4 +15,3 @@ export default { tools, providers, } - diff --git a/src/i18n/locales/en-US/providers.json b/src/i18n/locales/en-US/providers.json index 7a424be..7762ee7 100644 --- a/src/i18n/locales/en-US/providers.json +++ b/src/i18n/locales/en-US/providers.json @@ -73,4 +73,3 @@ } } } - diff --git a/src/i18n/locales/en-US/sidebar.json b/src/i18n/locales/en-US/sidebar.json index 5d93218..d36f6e7 100644 --- a/src/i18n/locales/en-US/sidebar.json +++ b/src/i18n/locales/en-US/sidebar.json @@ -32,4 +32,3 @@ "settings": "Settings" } } - diff --git a/src/i18n/locales/en-US/tools.json b/src/i18n/locales/en-US/tools.json index db4b06a..79346b2 100644 --- a/src/i18n/locales/en-US/tools.json +++ b/src/i18n/locales/en-US/tools.json @@ -44,4 +44,3 @@ "mergedSuffix": "Merged" } } - diff --git a/src/i18n/locales/zh-CN/index.ts b/src/i18n/locales/zh-CN/index.ts index 537c00e..5f23e47 100644 --- a/src/i18n/locales/zh-CN/index.ts +++ b/src/i18n/locales/zh-CN/index.ts @@ -15,4 +15,3 @@ export default { tools, providers, } - diff --git a/src/i18n/locales/zh-CN/providers.json b/src/i18n/locales/zh-CN/providers.json index 868dc83..1e23334 100644 --- a/src/i18n/locales/zh-CN/providers.json +++ b/src/i18n/locales/zh-CN/providers.json @@ -73,4 +73,3 @@ } } } - diff --git a/src/i18n/locales/zh-CN/sidebar.json b/src/i18n/locales/zh-CN/sidebar.json index b9dc3ef..a1196c0 100644 --- a/src/i18n/locales/zh-CN/sidebar.json +++ b/src/i18n/locales/zh-CN/sidebar.json @@ -32,4 +32,3 @@ "settings": "设置" } } - diff --git a/src/i18n/locales/zh-CN/tools.json b/src/i18n/locales/zh-CN/tools.json index 03c095b..2e54f63 100644 --- a/src/i18n/locales/zh-CN/tools.json +++ b/src/i18n/locales/zh-CN/tools.json @@ -44,4 +44,3 @@ "mergedSuffix": "合并" } } - diff --git a/src/pages/group-chat/components/member/MemberList.vue b/src/pages/group-chat/components/member/MemberList.vue index 96bae08..3b6f1e9 100644 --- a/src/pages/group-chat/components/member/MemberList.vue +++ b/src/pages/group-chat/components/member/MemberList.vue @@ -202,7 +202,13 @@ onMounted(() => {
- +
@@ -258,11 +264,7 @@ onMounted(() => { - +
@@ -334,7 +336,13 @@ onMounted(() => { class="flex items-center justify-between border-t border-gray-200 px-6 py-4 dark:border-gray-700" >

- {{ t('pagination', { start: (currentPage - 1) * pageSize + 1, end: Math.min(currentPage * pageSize, total), total: total }) }} + {{ + t('pagination', { + start: (currentPage - 1) * pageSize + 1, + end: Math.min(currentPage * pageSize, total), + total: total, + }) + }}

{

{{ t('modal.title') }}

- {{ t('modal.content', { name: deletingMember ? getDisplayName(deletingMember) : '', count: deletingMember?.messageCount.toLocaleString() }) }} + {{ + t('modal.content', { + name: deletingMember ? getDisplayName(deletingMember) : '', + count: deletingMember?.messageCount.toLocaleString(), + }) + }}

{{ t('modal.cancel') }} diff --git a/src/pages/group-chat/components/member/Relationships.vue b/src/pages/group-chat/components/member/Relationships.vue index 32e5000..1932cc6 100644 --- a/src/pages/group-chat/components/member/Relationships.vue +++ b/src/pages/group-chat/components/member/Relationships.vue @@ -153,7 +153,9 @@ watch(
- {{ t('oneWay.ratio', { value: Math.round(pair.ratio * 100) }) }} + + {{ t('oneWay.ratio', { value: Math.round(pair.ratio * 100) }) }} +
diff --git a/src/pages/home/components/FileListItem.vue b/src/pages/home/components/FileListItem.vue index e7fcc73..9f88232 100644 --- a/src/pages/home/components/FileListItem.vue +++ b/src/pages/home/components/FileListItem.vue @@ -42,4 +42,3 @@ defineProps<{
- diff --git a/src/pages/home/components/HomeFooter.vue b/src/pages/home/components/HomeFooter.vue index 706ec69..4f9cef2 100644 --- a/src/pages/home/components/HomeFooter.vue +++ b/src/pages/home/components/HomeFooter.vue @@ -199,10 +199,7 @@ watch(locale, () => {
- diff --git a/src/pages/home/components/MigrationModal.vue b/src/pages/home/components/MigrationModal.vue index 1a17b44..af13cc6 100644 --- a/src/pages/home/components/MigrationModal.vue +++ b/src/pages/home/components/MigrationModal.vue @@ -110,14 +110,7 @@ onMounted(async () => {
- + {{ t('home.migration.close') }} @@ -129,12 +122,16 @@ onMounted(async () => { :class="canClose ? 'flex-1' : 'w-full'" @click="handleMigration" > - {{ isMigrating ? t('home.migration.upgrading') : migrationError ? t('home.migration.retry') : t('home.migration.upgradeNow') }} + {{ + isMigrating + ? t('home.migration.upgrading') + : migrationError + ? t('home.migration.retry') + : t('home.migration.upgradeNow') + }}
- - diff --git a/src/pages/manage/components/BatchManageTab.vue b/src/pages/manage/components/BatchManageTab.vue index dba31ea..ccacc56 100644 --- a/src/pages/manage/components/BatchManageTab.vue +++ b/src/pages/manage/components/BatchManageTab.vue @@ -25,10 +25,7 @@ const filteredSessions = computed(() => { return sessions.value } const query = searchQuery.value.toLowerCase().trim() - return sessions.value.filter((s) => - s.name.toLowerCase().includes(query) || - s.platform.toLowerCase().includes(query) - ) + return sessions.value.filter((s) => s.name.toLowerCase().includes(query) || s.platform.toLowerCase().includes(query)) }) // 选中的会话 ID 集合 @@ -63,8 +60,7 @@ const selectedPlatform = computed(() => { // 全选状态(基于过滤后的列表) const isAllSelected = computed(() => { - return filteredSessions.value.length > 0 && - filteredSessions.value.every((s) => selectedIds.value.has(s.id)) + return filteredSessions.value.length > 0 && filteredSessions.value.every((s) => selectedIds.value.has(s.id)) }) // 部分选中状态(用于 indeterminate) @@ -107,7 +103,10 @@ function isSelected(id: string): boolean { // 格式化时间 function formatTime(timestamp: number): string { - return dayjs.unix(timestamp).locale(locale.value === 'zh-CN' ? 'zh-cn' : 'en').fromNow() + return dayjs + .unix(timestamp) + .locale(locale.value === 'zh-CN' ? 'zh-cn' : 'en') + .fromNow() } // 判断是否是私聊 @@ -139,7 +138,10 @@ const PLATFORM_CONFIG: Record = { qq: { label: 'QQ', class: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300' }, weixin: { label: '微信', class: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-300' }, discord: { label: 'Discord', class: 'bg-indigo-100 text-indigo-700 dark:bg-indigo-900/30 dark:text-indigo-300' }, - whatsapp: { label: 'WhatsApp', class: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300' }, + whatsapp: { + label: 'WhatsApp', + class: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300', + }, instagram: { label: 'Instagram', class: 'bg-pink-100 text-pink-700 dark:bg-pink-900/30 dark:text-pink-300' }, line: { label: 'LINE', class: 'bg-lime-100 text-lime-700 dark:bg-lime-900/30 dark:text-lime-300' }, unknown: { label: '未知', class: 'bg-gray-100 text-gray-700 dark:bg-gray-700 dark:text-gray-300' }, @@ -172,7 +174,7 @@ async function saveEdit() { const newName = editingName.value.trim() const session = sessions.value.find((s) => s.id === editingId.value) - + // 如果名称没变,不保存 if (session && session.name !== newName) { await sessionStore.renameSession(editingId.value, newName) @@ -306,7 +308,7 @@ async function confirmBatchDelete() { isDeleting.value = true try { const idsToDelete = Array.from(selectedIds.value) - + // 逐个删除 for (const id of idsToDelete) { await sessionStore.deleteSession(id) @@ -356,14 +358,17 @@ onMounted(() => { :label="t('tools.batchManage.selectAll')" @update:model-value="toggleSelectAll" /> - + {{ t('tools.batchManage.selected', { count: selectedIds.size }) }} - + {{ t('tools.batchManage.searchResult', { count: filteredSessions.length, total: sessions.length }) }}
@@ -371,23 +376,13 @@ onMounted(() => {
- + {{ t('tools.batchManage.merge') }} - + - + {{ t('tools.batchManage.delete') }}
@@ -410,7 +405,9 @@ onMounted(() => {
-
+
{{ t('tools.batchManage.columns.name') }}
@@ -426,17 +423,13 @@ onMounted(() => { class="flex cursor-pointer items-center gap-3 px-3 py-2 transition-colors hover:bg-gray-100 dark:hover:bg-gray-800" :class="[ isSelected(session.id) ? 'bg-pink-50 dark:bg-pink-900/20' : '', - index !== filteredSessions.length - 1 ? 'border-b border-gray-100 dark:border-gray-800' : '' + index !== filteredSessions.length - 1 ? 'border-b border-gray-100 dark:border-gray-800' : '', ]" @click="toggleSelect(session.id)" >
- +
@@ -489,7 +482,10 @@ onMounted(() => {
- + {{ getPlatformLabel(session.platform) }}
@@ -518,7 +514,7 @@ onMounted(() => { {{ t('tools.batchManage.mergeConfirmTitle') }}
- +

{{ t('tools.batchManage.mergeConfirmMessage', { count: selectedIds.size }) }}

@@ -526,7 +522,7 @@ onMounted(() => {
@@ -571,7 +567,7 @@ onMounted(() => { {{ t('tools.batchManage.confirmTitle') }}
- +

{{ t('tools.batchManage.confirmMessage', { count: selectedIds.size }) }}

diff --git a/src/pages/private-chat/components/ViewTab.vue b/src/pages/private-chat/components/ViewTab.vue index c220d1b..32d67dc 100644 --- a/src/pages/private-chat/components/ViewTab.vue +++ b/src/pages/private-chat/components/ViewTab.vue @@ -19,9 +19,7 @@ const props = defineProps<{ }>() // 子 Tab 配置(私聊只有消息视图) -const subTabs = computed(() => [ - { id: 'message', label: t('message'), icon: 'i-heroicons-chat-bubble-left-right' }, -]) +const subTabs = computed(() => [{ id: 'message', label: t('message'), icon: 'i-heroicons-chat-bubble-left-right' }]) const activeSubTab = ref('message') diff --git a/src/pages/private-chat/components/view/TimelineView.vue b/src/pages/private-chat/components/view/TimelineView.vue index b1205b3..5ef9b73 100644 --- a/src/pages/private-chat/components/view/TimelineView.vue +++ b/src/pages/private-chat/components/view/TimelineView.vue @@ -45,4 +45,3 @@ defineProps<{ } } - diff --git a/src/stores/embedding.ts b/src/stores/embedding.ts index cd84b56..0a75c20 100644 --- a/src/stores/embedding.ts +++ b/src/stores/embedding.ts @@ -82,7 +82,6 @@ export const useEmbeddingStore = defineStore('embedding', () => { } } - /** * 切换激活配置 */ @@ -166,4 +165,3 @@ export const useEmbeddingStore = defineStore('embedding', () => { refreshConfigs, } }) - diff --git a/src/stores/llm.ts b/src/stores/llm.ts index c449cde..bd9b4cd 100644 --- a/src/stores/llm.ts +++ b/src/stores/llm.ts @@ -147,4 +147,3 @@ export const useLLMStore = defineStore('llm', () => { getProviderName, } }) - diff --git a/src/stores/session.ts b/src/stores/session.ts index 8b1e204..f4ec66d 100644 --- a/src/stores/session.ts +++ b/src/stores/session.ts @@ -174,8 +174,8 @@ export const useSessionStore = defineStore( diagnosisSuggestion?: string }> { try { - const result = await window.chatApi.selectFile() - // 用户取消选择 + const result = await window.chatApi.selectFile() + // 用户取消选择 if (!result) { return { success: false, error: 'error.no_file_selected' } } @@ -565,7 +565,7 @@ export const useSessionStore = defineStore( // 智能命名:如果所有文件群名相同则用该名,否则用第一个文件的群名 const names = mergeFiles.value.map((f) => f.info?.name).filter(Boolean) const uniqueNames = [...new Set(names)] - const outputName = uniqueNames.length === 1 ? uniqueNames[0]! : (names[0] || '合并记录') + const outputName = uniqueNames.length === 1 ? uniqueNames[0]! : names[0] || '合并记录' const result = await window.mergeApi.mergeFiles({ filePaths, diff --git a/src/types/ai.ts b/src/types/ai.ts index b85ef3f..fcae2f8 100644 --- a/src/types/ai.ts +++ b/src/types/ai.ts @@ -47,4 +47,3 @@ export interface AIPromptSettings { * @deprecated 使用 PresetApplicableType 代替 */ export type PromptPresetChatType = 'group' | 'private' - diff --git a/src/types/analysis.ts b/src/types/analysis.ts index 416d583..71d631d 100644 --- a/src/types/analysis.ts +++ b/src/types/analysis.ts @@ -542,4 +542,3 @@ export interface KeywordTemplate { name: string keywords: string[] } - diff --git a/src/utils/conversationExport.ts b/src/utils/conversationExport.ts index 891e6f4..59c8483 100644 --- a/src/utils/conversationExport.ts +++ b/src/utils/conversationExport.ts @@ -125,4 +125,3 @@ export async function exportConversation( return result } - diff --git a/src/utils/sanitizeSummary.ts b/src/utils/sanitizeSummary.ts index cb88655..b7acce5 100644 --- a/src/utils/sanitizeSummary.ts +++ b/src/utils/sanitizeSummary.ts @@ -13,7 +13,9 @@ const DEFAULT_ALLOWED_ATTRS: Record = { const DEFAULT_ALLOWED_PROTOCOLS = ['http:', 'https:'] function normalizeAllowedTags(allowedTags?: string[]) { - return new Set((allowedTags && allowedTags.length > 0 ? allowedTags : DEFAULT_ALLOWED_TAGS).map((tag) => tag.toUpperCase())) + return new Set( + (allowedTags && allowedTags.length > 0 ? allowedTags : DEFAULT_ALLOWED_TAGS).map((tag) => tag.toUpperCase()) + ) } function normalizeAllowedAttrs(allowedAttrs?: Record) { @@ -46,7 +48,9 @@ export function sanitizeSummary(raw: string, options: SanitizeSummaryOptions = { const allowedTags = normalizeAllowedTags(options.allowedTags) const allowedAttrs = normalizeAllowedAttrs(options.allowedAttrs) const allowedProtocols = - options.allowedProtocols && options.allowedProtocols.length > 0 ? options.allowedProtocols : DEFAULT_ALLOWED_PROTOCOLS + options.allowedProtocols && options.allowedProtocols.length > 0 + ? options.allowedProtocols + : DEFAULT_ALLOWED_PROTOCOLS const parser = new DOMParser() const doc = parser.parseFromString(raw || '', 'text/html') diff --git a/src/utils/sqlExport.ts b/src/utils/sqlExport.ts index d3852d0..48a4c72 100644 --- a/src/utils/sqlExport.ts +++ b/src/utils/sqlExport.ts @@ -63,4 +63,3 @@ export async function exportSQLResult( return result } -