From bff3e05d93a8bcf86c2727366bb2bf5c82397b61 Mon Sep 17 00:00:00 2001 From: duanfuxiang Date: Mon, 7 Jul 2025 17:32:49 +0800 Subject: [PATCH] Add dependency on `remove-markdown`, update `package.json` and `pnpm-lock.yaml` files, optimize the search view component, enhance internationalization support, update user interaction prompts, and improve both user experience and code readability. --- package.json | 1 + pnpm-lock.yaml | 24 ++++-- src/components/chat-view/SearchView.tsx | 73 ++++++++++--------- .../chat-view/chat-input/SearchModeSelect.tsx | 14 ++-- src/database/modules/vector/vector-manager.ts | 10 +-- src/lang/locale/en.ts | 57 +++++++++++++++ src/lang/locale/zh-cn.ts | 57 +++++++++++++++ 7 files changed, 181 insertions(+), 55 deletions(-) diff --git a/package.json b/package.json index e0edccd..d20a818 100644 --- a/package.json +++ b/package.json @@ -120,6 +120,7 @@ "reconnecting-eventsource": "^1.6.4", "rehype-raw": "^7.0.0", "remark-gfm": "^4.0.0", + "remove-markdown": "^0.6.2", "shell-env": "^4.0.1", "simple-git": "^3.27.0", "smart-embed-model": "^1.0.7", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72ad084..b27d8da 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -210,6 +210,9 @@ importers: remark-gfm: specifier: ^4.0.0 version: 4.0.1 + remove-markdown: + specifier: ^0.6.2 + version: 0.6.2 shell-env: specifier: ^4.0.1 version: 4.0.1 @@ -577,8 +580,8 @@ packages: '@codemirror/language@6.11.2': resolution: {integrity: sha512-p44TsNArL4IVXDTbapUmEkAlvWs2CFQbcfc0ymDsis1kH2wh0gcY96AS29c/vp2d0y2Tquk1EDSaawpzilUiAw==} - '@codemirror/language@https://codeload.github.com/lishid/cm-language/tar.gz/6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67': - resolution: {tarball: https://codeload.github.com/lishid/cm-language/tar.gz/6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67} + '@codemirror/language@git+https://git@github.com:lishid/cm-language.git#6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67': + resolution: {commit: 6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67, repo: git@github.com:lishid/cm-language.git, type: git} version: 6.10.8 '@codemirror/lint@0.20.3': @@ -5451,8 +5454,8 @@ packages: '@codemirror/state': ^6.0.0 '@codemirror/view': ^6.0.0 - obsidian@https://codeload.github.com/obsidianmd/obsidian-api/tar.gz/103ff76a0712a02dd0da28646b5a6b0ba6686d66: - resolution: {tarball: https://codeload.github.com/obsidianmd/obsidian-api/tar.gz/103ff76a0712a02dd0da28646b5a6b0ba6686d66} + obsidian@git+https://git@github.com:obsidianmd/obsidian-api.git#103ff76a0712a02dd0da28646b5a6b0ba6686d66: + resolution: {commit: 103ff76a0712a02dd0da28646b5a6b0ba6686d66, repo: git@github.com:obsidianmd/obsidian-api.git, type: git} version: 1.8.7 peerDependencies: '@codemirror/state': ^6.0.0 @@ -5921,6 +5924,9 @@ packages: remove-markdown@0.5.5: resolution: {integrity: sha512-lMR8tOtDqazFT6W2bZidoXwkptMdF3pCxpri0AEokHg0sZlC2GdoLqnoaxsEj1o7/BtXV1MKtT3YviA1t7rW7g==} + remove-markdown@0.6.2: + resolution: {integrity: sha512-EijDXJZbzpGbQBd852ViUzcqgpMujthM+SAEHiWCMcZonRbZ+xViWKLJA/vrwbDwYdxrs1aFDjpBhcGrZoJRGA==} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -7083,7 +7089,7 @@ snapshots: '@lezer/lr': 1.4.2 style-mod: 4.1.2 - '@codemirror/language@https://codeload.github.com/lishid/cm-language/tar.gz/6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67': + '@codemirror/language@git+https://git@github.com:lishid/cm-language.git#6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67': dependencies: '@codemirror/state': 6.5.2 '@codemirror/view': 6.38.0 @@ -12598,7 +12604,7 @@ snapshots: obsidian-daily-notes-interface@0.8.4(@codemirror/state@6.5.2)(@codemirror/view@6.38.0): dependencies: - obsidian: https://codeload.github.com/obsidianmd/obsidian-api/tar.gz/103ff76a0712a02dd0da28646b5a6b0ba6686d66(@codemirror/state@6.5.2)(@codemirror/view@6.38.0) + obsidian: git+https://git@github.com:obsidianmd/obsidian-api.git#103ff76a0712a02dd0da28646b5a6b0ba6686d66(@codemirror/state@6.5.2)(@codemirror/view@6.38.0) tslib: 2.1.0 transitivePeerDependencies: - '@codemirror/state' @@ -12606,7 +12612,7 @@ snapshots: obsidian-dataview@0.5.68: dependencies: - '@codemirror/language': https://codeload.github.com/lishid/cm-language/tar.gz/6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67 + '@codemirror/language': git+https://git@github.com:lishid/cm-language.git#6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67 '@codemirror/state': 6.5.2 '@codemirror/view': 6.38.0 emoji-regex: 10.4.0 @@ -12625,7 +12631,7 @@ snapshots: '@types/codemirror': 5.60.8 moment: 2.29.4 - obsidian@https://codeload.github.com/obsidianmd/obsidian-api/tar.gz/103ff76a0712a02dd0da28646b5a6b0ba6686d66(@codemirror/state@6.5.2)(@codemirror/view@6.38.0): + obsidian@git+https://git@github.com:obsidianmd/obsidian-api.git#103ff76a0712a02dd0da28646b5a6b0ba6686d66(@codemirror/state@6.5.2)(@codemirror/view@6.38.0): dependencies: '@codemirror/state': 6.5.2 '@codemirror/view': 6.38.0 @@ -13233,6 +13239,8 @@ snapshots: remove-markdown@0.5.5: {} + remove-markdown@0.6.2: {} + require-directory@2.1.1: {} require-from-string@2.0.2: {} diff --git a/src/components/chat-view/SearchView.tsx b/src/components/chat-view/SearchView.tsx index 5a5751d..c012053 100644 --- a/src/components/chat-view/SearchView.tsx +++ b/src/components/chat-view/SearchView.tsx @@ -10,6 +10,7 @@ import { useTrans } from '../../contexts/TransContext' import { Workspace } from '../../database/json/workspace/types' import { WorkspaceManager } from '../../database/json/workspace/WorkspaceManager' import { SelectVector } from '../../database/schema' +import { t } from '../../lang/helpers' import { Mentionable } from '../../types/mentionable' import { getFilesWithTag } from '../../utils/glob-utils' import { openMarkdownFile } from '../../utils/obsidian' @@ -459,7 +460,7 @@ const SearchView = () => { h5: ({ children }) =>
{children}
, h6: ({ children }) =>
{children}
, // 移除图片显示,避免布局问题 - img: () => [图片], + img: () => {t('semanticSearch.imagePlaceholder')}, // 代码块样式 code: ({ children, inline }: { children: React.ReactNode; inline?: boolean;[key: string]: unknown }) => { if (inline) { @@ -619,7 +620,7 @@ const SearchView = () => { {/* 头部信息 */}
-

语义索引

+

{t('semanticSearch.title')}

{/* 统计信息 */} @@ -628,20 +629,20 @@ const SearchView = () => {
{statisticsInfo.totalChunks} - 个向量块 + {t('semanticSearch.vectorBlocks')}
📄 {statisticsInfo.totalFiles} - 文件 + {t('semanticSearch.files')}
)}
- 嵌入模型: + {t('semanticSearch.embeddingModel')}
@@ -649,9 +650,9 @@ const SearchView = () => { onClick={handleInitWorkspaceRAG} disabled={isInitializingRAG || isDeleting || isSearching} className="obsidian-search-init-btn" - title={statisticsInfo && (statisticsInfo.totalFiles > 0 || statisticsInfo.totalChunks > 0) ? '更新索引' : '初始化索引'} + title={statisticsInfo && (statisticsInfo.totalFiles > 0 || statisticsInfo.totalChunks > 0) ? t('semanticSearch.updateIndex') : t('semanticSearch.initializeIndex')} > - {isInitializingRAG ? '正在初始化...' : (statisticsInfo && (statisticsInfo.totalFiles > 0 || statisticsInfo.totalChunks > 0) ? '更新索引' : '初始化索引')} + {isInitializingRAG ? t('semanticSearch.initializing') : (statisticsInfo && (statisticsInfo.totalFiles > 0 || statisticsInfo.totalChunks > 0) ? t('semanticSearch.updateIndex') : t('semanticSearch.initializeIndex'))}
@@ -662,15 +663,15 @@ const SearchView = () => { {isInitializingRAG && (
-

正在初始化工作区 RAG 向量索引

-

为当前工作区的文件建立向量索引,提高搜索精度

+

{t('semanticSearch.initializingWorkspace')}

+

{t('semanticSearch.initializingDescription')}

{ragInitProgress && ragInitProgress.type === 'indexing' && ragInitProgress.indexProgress && (
- 建立向量索引 + {t('semanticSearch.buildingVectorIndex')} - {ragInitProgress.indexProgress.completedChunks} / {ragInitProgress.indexProgress.totalChunks} 块 + {ragInitProgress.indexProgress.completedChunks} / {ragInitProgress.indexProgress.totalChunks} {t('semanticSearch.blocks')}
@@ -683,7 +684,7 @@ const SearchView = () => {
- 共 {ragInitProgress.indexProgress.totalFiles} 个文件 + {t('semanticSearch.totalFiles', { count: ragInitProgress.indexProgress.totalFiles })}
{Math.round((ragInitProgress.indexProgress.completedChunks / Math.max(ragInitProgress.indexProgress.totalChunks, 1)) * 100)}% @@ -700,9 +701,9 @@ const SearchView = () => {
- - 工作区 RAG 向量索引初始化完成: {ragInitSuccess.workspaceName} - + + {t('semanticSearch.initializationComplete', { workspaceName: ragInitSuccess.workspaceName })} +
@@ -787,31 +788,31 @@ const SearchView = () => {
-

{statisticsInfo && (statisticsInfo.totalFiles > 0 || statisticsInfo.totalChunks > 0) ? '更新工作区索引' : '初始化工作区索引'}

+

{statisticsInfo && (statisticsInfo.totalFiles > 0 || statisticsInfo.totalChunks > 0) ? t('semanticSearch.initConfirm.updateTitle') : t('semanticSearch.initConfirm.initTitle')}

{statisticsInfo && (statisticsInfo.totalFiles > 0 || statisticsInfo.totalChunks > 0) - ? '将更新当前工作区的向量索引,重新处理所有文件以确保索引最新。' - : '将为当前工作区的所有文件建立向量索引,这将提高语义搜索的准确性。' + ? t('semanticSearch.initConfirm.updateMessage') + : t('semanticSearch.initConfirm.initMessage') }

- 嵌入模型: + {t('semanticSearch.initConfirm.embeddingModelLabel')} {settings.embeddingModelId}
- 工作区: + {t('semanticSearch.initConfirm.workspaceLabel')} - {settings.workspace === 'vault' ? '整个 Vault' : settings.workspace} + {settings.workspace === 'vault' ? t('semanticSearch.initConfirm.entireVault') : settings.workspace}

- 此操作可能需要几分钟时间,具体取决于文件数量和大小。 + {t('semanticSearch.initConfirm.warning')}

@@ -819,13 +820,13 @@ const SearchView = () => { onClick={cancelRAGInitConfirm} className="obsidian-confirm-dialog-cancel-btn" > - 取消 + {t('semanticSearch.initConfirm.cancel')}
@@ -835,7 +836,7 @@ const SearchView = () => { {/* 搜索进度 */} {isSearching && (
- 正在搜索... + {t('semanticSearch.searching')}
)} @@ -1044,7 +1045,7 @@ const SearchView = () => { (searchMode === 'all' && allGroupedResults.length === 0) ) && (
-

未找到相关结果

+

{t('semanticSearch.noResults')}

)}
diff --git a/src/components/chat-view/chat-input/SearchModeSelect.tsx b/src/components/chat-view/chat-input/SearchModeSelect.tsx index 63a9c38..2ef222a 100644 --- a/src/components/chat-view/chat-input/SearchModeSelect.tsx +++ b/src/components/chat-view/chat-input/SearchModeSelect.tsx @@ -2,6 +2,8 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu' import { ChevronDown, ChevronUp, FileText, Lightbulb, Globe } from 'lucide-react' import { useState } from 'react' +import { t } from '../../../lang/helpers' + interface SearchModeSelectProps { searchMode: 'notes' | 'insights' | 'all' onSearchModeChange: (mode: 'notes' | 'insights' | 'all') => void @@ -13,21 +15,21 @@ export function SearchModeSelect({ searchMode, onSearchModeChange }: SearchModeS const searchModes = [ { value: 'all' as const, - name: '全部', + name: t('semanticSearch.searchMode.all'), icon: , - description: '聚合搜索原始笔记和 AI 洞察' + description: t('semanticSearch.searchMode.allDescription') }, { value: 'notes' as const, - name: '原始笔记', + name: t('semanticSearch.searchMode.notes'), icon: , - description: '搜索原始笔记内容' + description: t('semanticSearch.searchMode.notesDescription') }, { value: 'insights' as const, - name: 'AI 洞察', + name: t('semanticSearch.searchMode.insights'), icon: , - description: '搜索 AI 洞察内容' + description: t('semanticSearch.searchMode.insightsDescription') } ] diff --git a/src/database/modules/vector/vector-manager.ts b/src/database/modules/vector/vector-manager.ts index fa807d9..fdb7d83 100644 --- a/src/database/modules/vector/vector-manager.ts +++ b/src/database/modules/vector/vector-manager.ts @@ -1,9 +1,9 @@ import { backOff } from 'exponential-backoff'; import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter'; -import removeMarkdown from "markdown-to-text"; import { minimatch } from 'minimatch'; import { App, Notice, TFile } from 'obsidian'; import pLimit from 'p-limit'; +import removeMarkdown from 'remove-markdown'; import { IndexProgress } from '../../../components/chat-view/QueryProgress'; import { @@ -14,10 +14,10 @@ import { } from '../../../core/llm/exception'; import { InsertVector, SelectVector } from '../../../database/schema'; import { EmbeddingModel } from '../../../types/embedding'; -import { openSettingsModalWithError } from '../../../utils/open-settings-modal'; import { getFilesWithTag } from '../../../utils/glob-utils'; -import { Workspace } from '../../json/workspace/types'; +import { openSettingsModalWithError } from '../../../utils/open-settings-modal'; import { DBManager } from '../../database-manager'; +import { Workspace } from '../../json/workspace/types'; import { VectorRepository } from './vector-repository'; @@ -256,7 +256,7 @@ export class VectorManager { async () => { // 在嵌入之前处理 markdown const cleanedBatchData = batchChunks.map(chunk => { - const cleanContent = removeMarkdown(chunk.content).replace(/\0/g, '') + const cleanContent = removeMarkdown(chunk.content) return { chunk, cleanContent } }).filter(({ cleanContent }) => cleanContent && cleanContent.trim().length > 0) @@ -539,7 +539,7 @@ export class VectorManager { async () => { // 在嵌入之前处理 markdown,只处理一次 const cleanedBatchData = batchChunks.map(chunk => { - const cleanContent = removeMarkdown(chunk.content).replace(/\0/g, '') + const cleanContent = removeMarkdown(chunk.content) return { chunk, cleanContent } }).filter(({ cleanContent }) => cleanContent && cleanContent.trim().length > 0) diff --git a/src/lang/locale/en.ts b/src/lang/locale/en.ts index dc0494f..7ac0f65 100644 --- a/src/lang/locale/en.ts +++ b/src/lang/locale/en.ts @@ -497,6 +497,63 @@ export default { toolNoDescription: "No description", useMcpToolFrom: "Use MCP tool from", }, + semanticSearch: { + title: "Semantic Index", + embeddingModel: "Embedding model:", + vectorBlocks: "vector blocks", + files: "files", + initializeIndex: "Initialize index", + updateIndex: "Update index", + initializing: "Initializing...", + initializingWorkspace: "Initializing workspace RAG vector index", + initializingDescription: "Building vector index for files in the current workspace to improve search accuracy", + buildingVectorIndex: "Building vector index", + blocks: "blocks", + totalFiles: "Total {count} files", + initializationComplete: "Workspace RAG vector index initialization complete: {workspaceName}", + searchPlaceholder: "Semantic search (press Enter to search)...", + searching: "Searching...", + noResults: "No relevant results found", + imagePlaceholder: "[Image]", + // Search mode + searchMode: { + all: "All", + allDescription: "Search both original notes and AI insights", + notes: "Original Notes", + notesDescription: "Search original note content", + insights: "AI Insights", + insightsDescription: "Search AI insight content" + }, + // Statistics + stats: { + filesAndBlocks: "{files} files, {blocks} blocks", + filesAndInsights: "{files} files, {insights} insights", + filesBlocksAndInsights: "{files} files, {blocks} blocks, {insights} insights" + }, + // Confirmation dialogs + deleteConfirm: { + title: "Clear workspace index", + message: "This will clear all vector index data for the current workspace.", + warning: "This action cannot be undone. After clearing, you need to reinitialize the index to perform semantic search.", + workspaceLabel: "Workspace:", + entireVault: "Entire Vault", + cancel: "Cancel", + confirm: "Confirm Clear" + }, + initConfirm: { + initTitle: "Initialize workspace index", + updateTitle: "Update workspace index", + initMessage: "This will build vector index for all files in the current workspace, which will improve semantic search accuracy.", + updateMessage: "This will update the vector index for the current workspace, reprocessing all files to ensure the index is up-to-date.", + embeddingModelLabel: "Embedding model:", + workspaceLabel: "Workspace:", + entireVault: "Entire Vault", + warning: "This operation may take several minutes, depending on the number and size of files.", + cancel: "Cancel", + startInit: "Start Initialize", + startUpdate: "Start Update" + } + }, insights: { title: "AI Insights", initializeInsights: "Initialize Insights", diff --git a/src/lang/locale/zh-cn.ts b/src/lang/locale/zh-cn.ts index a89a92a..a63e7f8 100644 --- a/src/lang/locale/zh-cn.ts +++ b/src/lang/locale/zh-cn.ts @@ -499,6 +499,63 @@ export default { useMcpToolFrom: "使用来自以下的 MCP 工具:", } }, + semanticSearch: { + title: "语义索引", + embeddingModel: "嵌入模型:", + vectorBlocks: "个向量块", + files: "文件", + initializeIndex: "初始化索引", + updateIndex: "更新索引", + initializing: "正在初始化...", + initializingWorkspace: "正在初始化工作区 RAG 向量索引", + initializingDescription: "为当前工作区的文件建立向量索引,提高搜索精度", + buildingVectorIndex: "建立向量索引", + blocks: "块", + totalFiles: "共 {count} 个文件", + initializationComplete: "工作区 RAG 向量索引初始化完成: {workspaceName}", + searchPlaceholder: "语义搜索(按回车键搜索)...", + searching: "正在搜索...", + noResults: "未找到相关结果", + imagePlaceholder: "[图片]", + // 搜索模式 + searchMode: { + all: "全部", + allDescription: "聚合搜索原始笔记和 AI 洞察", + notes: "原始笔记", + notesDescription: "搜索原始笔记内容", + insights: "AI 洞察", + insightsDescription: "搜索 AI 洞察内容" + }, + // 统计信息 + stats: { + filesAndBlocks: "{files} 个文件,{blocks} 个块", + filesAndInsights: "{files} 个文件,{insights} 个洞察", + filesBlocksAndInsights: "{files} 个文件,{blocks} 个块,{insights} 个洞察" + }, + // 确认对话框 + deleteConfirm: { + title: "清除工作区索引", + message: "将清除当前工作区的所有向量索引数据。", + warning: "此操作无法撤销,清除后需要重新初始化索引才能进行语义搜索。", + workspaceLabel: "工作区:", + entireVault: "整个 Vault", + cancel: "取消", + confirm: "确认清除" + }, + initConfirm: { + initTitle: "初始化工作区索引", + updateTitle: "更新工作区索引", + initMessage: "将为当前工作区的所有文件建立向量索引,这将提高语义搜索的准确性。", + updateMessage: "将更新当前工作区的向量索引,重新处理所有文件以确保索引最新。", + embeddingModelLabel: "嵌入模型:", + workspaceLabel: "工作区:", + entireVault: "整个 Vault", + warning: "此操作可能需要几分钟时间,具体取决于文件数量和大小。", + cancel: "取消", + startInit: "开始初始化", + startUpdate: "开始更新" + } + }, insights: { title: "AI 洞察", initializeInsights: "初始化洞察",