From a2fcb7c20f6a4b65dd6159c7bdbf07b402d7857a Mon Sep 17 00:00:00 2001 From: duanfuxiang Date: Sat, 5 Jul 2025 16:24:49 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20TransEngine=20=E4=BB=A5?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=B5=8C=E5=85=A5=E7=AE=A1=E7=90=86=E5=99=A8?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E6=97=A5=E5=BF=97=E8=BE=93=E5=87=BA?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=E5=88=A0=E9=99=A4=E7=A1=AE=E8=AE=A4?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E6=A1=86=E5=8A=9F=E8=83=BD=EF=BC=8C=E6=94=B9?= =?UTF-8?q?=E8=BF=9B=20ChatView=20=E7=BB=84=E4=BB=B6=E7=9A=84=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E7=AE=A1=E7=90=86=E5=92=8C=E4=B8=8A=E4=B8=8B=E6=96=87?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E5=A4=84=E7=90=86=EF=BC=8C=E7=A1=AE=E4=BF=9D?= =?UTF-8?q?=E6=9B=B4=E5=A5=BD=E7=9A=84=E7=94=A8=E6=88=B7=E4=BD=93=E9=AA=8C?= =?UTF-8?q?=E5=92=8C=E4=BB=A3=E7=A0=81=E5=8F=AF=E8=AF=BB=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/chat-view/ChatView.tsx | 80 +++++------ src/components/chat-view/InsightView.tsx | 167 ++++++++++++++++++++++- src/core/transformations/trans-engine.ts | 38 +++++- src/main.ts | 2 +- 4 files changed, 242 insertions(+), 45 deletions(-) diff --git a/src/components/chat-view/ChatView.tsx b/src/components/chat-view/ChatView.tsx index 5da7a15..9bc78f9 100644 --- a/src/components/chat-view/ChatView.tsx +++ b/src/components/chat-view/ChatView.tsx @@ -630,17 +630,17 @@ const Chat = forwardRef((props, ref) => { if (settings.workspace && settings.workspace !== 'vault') { currentWorkspace = await workspaceManager.findByName(String(settings.workspace)) } - + const files = await listFilesAndFolders( - app.vault, - toolArgs.filepath, - toolArgs.recursive, + app.vault, + toolArgs.filepath, + toolArgs.recursive, currentWorkspace || undefined, app ) - - const contextInfo = currentWorkspace - ? `workspace '${currentWorkspace.name}'` + + const contextInfo = currentWorkspace + ? `workspace '${currentWorkspace.name}'` : toolArgs.filepath || 'vault root' const formattedContent = `[list_files for '${contextInfo}'] Result:\n${files.join('\n')}\n`; return { @@ -710,7 +710,7 @@ const Chat = forwardRef((props, ref) => { if (settings.workspace && settings.workspace !== 'vault') { currentWorkspace = await workspaceManager.findByName(String(settings.workspace)) } - + const snippets = await semanticSearchFiles( await getRAGEngine(), toolArgs.query, @@ -719,9 +719,9 @@ const Chat = forwardRef((props, ref) => { app, await getTransEngine() ) - - const contextInfo = currentWorkspace - ? `workspace '${currentWorkspace.name}'` + + const contextInfo = currentWorkspace + ? `workspace '${currentWorkspace.name}'` : toolArgs.filepath || 'vault' const formattedContent = `[semantic_search_files for '${contextInfo}'] Result:\n${snippets}\n`; return { @@ -842,14 +842,14 @@ const Chat = forwardRef((props, ref) => { // 执行 Dataview 查询 const result = await dataviewManager.executeQuery(toolArgs.query) - + let formattedContent: string; if (result.success) { formattedContent = `[dataview_query] 查询成功:\n${result.data}`; } else { formattedContent = `[dataview_query] 查询失败:\n${result.error}`; } - + return { type: 'dataview_query', applyMsgId, @@ -893,7 +893,7 @@ const Chat = forwardRef((props, ref) => { // Build the result message let formattedContent = `[${toolArgs.transformation}] transformation complete:\n\n${transformationResult.result}`; - + if (transformationResult.truncated) { formattedContent += `\n\n*Note: The original content was too long (${transformationResult.originalTokens} tokens) and was truncated to ${transformationResult.processedTokens} tokens for processing.*`; } @@ -930,7 +930,7 @@ const Chat = forwardRef((props, ref) => { } else if (toolArgs.type === 'manage_files') { try { const results: string[] = []; - + // 处理每个文件操作 for (const operation of toolArgs.operations) { switch (operation.action) { @@ -945,7 +945,7 @@ const Chat = forwardRef((props, ref) => { } } break; - + case 'move': if (operation.source_path && operation.destination_path) { // 使用 getAbstractFileByPath 而不是 getFileByPath,这样可以获取文件和文件夹 @@ -967,7 +967,7 @@ const Chat = forwardRef((props, ref) => { } } break; - + case 'delete': if (operation.path) { // 使用 getAbstractFileByPath 而不是 getFileByPath @@ -989,7 +989,7 @@ const Chat = forwardRef((props, ref) => { } } break; - + case 'copy': if (operation.source_path && operation.destination_path) { // 文件夹复制比较复杂,需要递归处理 @@ -1016,7 +1016,7 @@ const Chat = forwardRef((props, ref) => { } } break; - + case 'rename': if (operation.path && operation.new_name) { // 使用 getAbstractFileByPath 而不是 getFileByPath @@ -1031,14 +1031,14 @@ const Chat = forwardRef((props, ref) => { } } break; - + default: results.push(`❌ 不支持的操作类型: ${String(operation.action)}`); } } - + const formattedContent = `[manage_files] 文件管理操作结果:\n${results.join('\n')}`; - + return { type: 'manage_files', applyMsgId, @@ -1165,15 +1165,15 @@ const Chat = forwardRef((props, ref) => { } const activeFile = app.workspace.getActiveFile() - + // 🎯 关键优化:只有当活动文件真正发生变化时才更新 if (activeFile === currentActiveFileRef.current) { return // 文件没有变化,不需要更新 } - + // 更新文件引用 currentActiveFileRef.current = activeFile - + if (!activeFile) return const mentionable: Omit = { @@ -1312,15 +1312,15 @@ const Chat = forwardRef((props, ref) => { + - {/* main view */} diff --git a/src/components/chat-view/InsightView.tsx b/src/components/chat-view/InsightView.tsx index 98a847d..0f5ef20 100644 --- a/src/components/chat-view/InsightView.tsx +++ b/src/components/chat-view/InsightView.tsx @@ -50,6 +50,8 @@ const InsightView = () => { // 删除洞察状态 const [isDeleting, setIsDeleting] = useState(false) const [deletingInsightId, setDeletingInsightId] = useState(null) + // 确认对话框状态 + const [showDeleteConfirm, setShowDeleteConfirm] = useState(false) const loadInsights = useCallback(async () => { setIsLoading(true) @@ -243,6 +245,11 @@ const InsightView = () => { } }, [getTransEngine, settings, workspaceManager, loadInsights]) + // 确认删除工作区洞察 + const handleDeleteWorkspaceInsights = useCallback(() => { + setShowDeleteConfirm(true) + }, []) + // 删除工作区洞察 const deleteWorkspaceInsights = useCallback(async () => { setIsDeleting(true) @@ -280,6 +287,17 @@ const InsightView = () => { } }, [getTransEngine, settings, workspaceManager, loadInsights]) + // 确认删除工作区洞察 + const confirmDeleteWorkspaceInsights = useCallback(async () => { + setShowDeleteConfirm(false) + await deleteWorkspaceInsights() + }, [deleteWorkspaceInsights]) + + // 取消删除确认 + const cancelDeleteConfirm = useCallback(() => { + setShowDeleteConfirm(false) + }, []) + // 删除单个洞察 const deleteSingleInsight = useCallback(async (insightId: number) => { setDeletingInsightId(insightId) @@ -482,7 +500,7 @@ const InsightView = () => { {isInitializing ? '初始化中...' : '初始化洞察'} + + + + + )} + {/* 洞察结果 */}
{!isLoading && insightGroupedResults.length > 0 && ( @@ -1098,6 +1152,117 @@ const InsightView = () => { color: var(--text-faint); font-style: italic; } + + /* 确认对话框样式 */ + .obsidian-confirm-dialog-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + } + + .obsidian-confirm-dialog { + background-color: var(--background-primary); + border: 1px solid var(--background-modifier-border); + border-radius: var(--radius-l); + box-shadow: var(--shadow-l); + max-width: 400px; + width: 90%; + max-height: 80vh; + overflow: hidden; + } + + .obsidian-confirm-dialog-header { + padding: 16px 20px; + border-bottom: 1px solid var(--background-modifier-border); + background-color: var(--background-secondary); + } + + .obsidian-confirm-dialog-header h3 { + margin: 0; + color: var(--text-normal); + font-size: var(--font-ui-large); + font-weight: 600; + } + + .obsidian-confirm-dialog-body { + padding: 20px; + color: var(--text-normal); + font-size: var(--font-ui-medium); + line-height: 1.5; + } + + .obsidian-confirm-dialog-body p { + margin: 0 0 12px 0; + } + + .obsidian-confirm-dialog-warning { + border: 1px solid var(--background-modifier-border); + border-radius: var(--radius-s); + padding: 12px; + margin: 12px 0; + color: var(--text-error); + font-size: var(--font-ui-small); + font-weight: 500; + } + + .obsidian-confirm-dialog-scope { + background-color: var(--background-secondary); + border: 1px solid var(--background-modifier-border); + border-radius: var(--radius-s); + padding: 8px 12px; + margin: 12px 0 0 0; + font-size: var(--font-ui-small); + color: var(--text-muted); + } + + .obsidian-confirm-dialog-footer { + padding: 16px 20px; + border-top: 1px solid var(--background-modifier-border); + background-color: var(--background-secondary); + display: flex; + justify-content: flex-end; + gap: 12px; + } + + .obsidian-confirm-dialog-cancel-btn { + padding: 8px 16px; + background-color: var(--interactive-normal); + border: 1px solid var(--background-modifier-border); + border-radius: var(--radius-s); + color: var(--text-normal); + font-size: var(--font-ui-small); + cursor: pointer; + transition: all 0.2s ease; + font-weight: 500; + } + + .obsidian-confirm-dialog-cancel-btn:hover { + background-color: var(--interactive-hover); + } + + .obsidian-confirm-dialog-confirm-btn { + padding: 8px 16px; + background-color: #dc3545; + border: 1px solid #dc3545; + border-radius: var(--radius-s); + color: white; + font-size: var(--font-ui-small); + cursor: pointer; + transition: all 0.2s ease; + font-weight: 500; + } + + .obsidian-confirm-dialog-confirm-btn:hover { + background-color: #c82333; + border-color: #c82333; + } `}
diff --git a/src/core/transformations/trans-engine.ts b/src/core/transformations/trans-engine.ts index 289c92e..3d48ccd 100644 --- a/src/core/transformations/trans-engine.ts +++ b/src/core/transformations/trans-engine.ts @@ -20,6 +20,15 @@ import { SIMPLE_SUMMARY_DESCRIPTION, SIMPLE_SUMMARY_PROMPT } from '../prompts/tr import { TABLE_OF_CONTENTS_DESCRIPTION, TABLE_OF_CONTENTS_PROMPT } from '../prompts/transformations/table-of-contents'; import { getEmbeddingModel } from '../rag/embedding'; +// EmbeddingManager 类型定义 +type EmbeddingManager = { + modelLoaded: boolean + currentModel: string | null + loadModel(modelId: string, useGpu: boolean): Promise + embed(text: string): Promise<{ vec: number[] }> + embedBatch(texts: string[]): Promise<{ vec: number[] }[]> +} + /** * 并发控制工具类 */ @@ -298,21 +307,24 @@ export class TransEngine { private llmManager: LLMManager; private insightManager: InsightManager | null = null; private embeddingModel: EmbeddingModel | null = null; + private embeddingManager?: EmbeddingManager; constructor( app: App, settings: InfioSettings, dbManager: DBManager, + embeddingManager?: EmbeddingManager, ) { this.app = app; this.settings = settings; this.llmManager = new LLMManager(settings); this.insightManager = dbManager.getInsightManager(); + this.embeddingManager = embeddingManager; // 初始化 embedding model if (settings.embeddingModelId && settings.embeddingModelId.trim() !== '') { try { - this.embeddingModel = getEmbeddingModel(settings); + this.embeddingModel = getEmbeddingModel(settings, embeddingManager); } catch (error) { console.warn('Failed to initialize embedding model:', error); this.embeddingModel = null; @@ -334,7 +346,7 @@ export class TransEngine { // 重新初始化 embedding model if (settings.embeddingModelId && settings.embeddingModelId.trim() !== '') { try { - this.embeddingModel = getEmbeddingModel(settings); + this.embeddingModel = getEmbeddingModel(settings, this.embeddingManager); } catch (error) { console.warn('Failed to initialize embedding model:', error); this.embeddingModel = null; @@ -395,7 +407,12 @@ export class TransEngine { > { // 如果没有必要的参数,跳过缓存检查 if (!this.embeddingModel || !this.insightManager) { - console.log("no embeddingModel or insightManager"); + console.log("TransEngine: 跳过缓存检查"); + console.log("embeddingModel:", this.embeddingModel ? "已初始化" : "未初始化"); + console.log("insightManager:", this.insightManager ? "已初始化" : "未初始化"); + console.log("embeddingModelId:", this.settings.embeddingModelId); + console.log("embeddingModelProvider:", this.settings.embeddingModelProvider); + console.log("提示:请在插件设置中配置嵌入模型,或点击'一键配置'按钮"); return { success: true, foundCache: false @@ -488,6 +505,11 @@ export class TransEngine { contentType: 'document' | 'tag' | 'folder' ): Promise { if (!this.embeddingModel || !this.insightManager) { + console.log("TransEngine: 无法保存到数据库"); + console.log("embeddingModel:", this.embeddingModel ? "已初始化" : "未初始化"); + console.log("insightManager:", this.insightManager ? "已初始化" : "未初始化"); + console.log("embeddingModelId:", this.settings.embeddingModelId); + console.log("embeddingModelProvider:", this.settings.embeddingModelProvider); return; } @@ -1027,6 +1049,11 @@ export class TransEngine { > { if (!this.embeddingModel || !this.insightManager) { console.warn('TransEngine: embedding model or insight manager not available') + console.log("embeddingModel:", this.embeddingModel ? "已初始化" : "未初始化"); + console.log("insightManager:", this.insightManager ? "已初始化" : "未初始化"); + console.log("embeddingModelId:", this.settings.embeddingModelId); + console.log("embeddingModelProvider:", this.settings.embeddingModelProvider); + console.log("提示:请在插件设置中配置嵌入模型,或点击'一键配置'按钮"); return [] } @@ -1082,6 +1109,11 @@ export class TransEngine { async getAllInsights(): Promise[]> { if (!this.embeddingModel || !this.insightManager) { console.warn('TransEngine: embedding model or insight manager not available') + console.log("embeddingModel:", this.embeddingModel ? "已初始化" : "未初始化"); + console.log("insightManager:", this.insightManager ? "已初始化" : "未初始化"); + console.log("embeddingModelId:", this.settings.embeddingModelId); + console.log("embeddingModelProvider:", this.settings.embeddingModelProvider); + console.log("提示:请在插件设置中配置嵌入模型,或点击'一键配置'按钮"); return [] } diff --git a/src/main.ts b/src/main.ts index 25b1457..efb49e5 100644 --- a/src/main.ts +++ b/src/main.ts @@ -640,7 +640,7 @@ export default class InfioPlugin extends Plugin { if (!this.transEngineInitPromise) { this.transEngineInitPromise = (async () => { const dbManager = await this.getDbManager() - this.transEngine = new TransEngine(this.app, this.settings, dbManager) + this.transEngine = new TransEngine(this.app, this.settings, dbManager, this.embeddingManager) return this.transEngine })() }