mirror of
https://github.com/EthanMarti/infio-copilot.git
synced 2026-01-16 16:31:56 +00:00
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.
This commit is contained in:
parent
c89186a40d
commit
bff3e05d93
@ -120,6 +120,7 @@
|
|||||||
"reconnecting-eventsource": "^1.6.4",
|
"reconnecting-eventsource": "^1.6.4",
|
||||||
"rehype-raw": "^7.0.0",
|
"rehype-raw": "^7.0.0",
|
||||||
"remark-gfm": "^4.0.0",
|
"remark-gfm": "^4.0.0",
|
||||||
|
"remove-markdown": "^0.6.2",
|
||||||
"shell-env": "^4.0.1",
|
"shell-env": "^4.0.1",
|
||||||
"simple-git": "^3.27.0",
|
"simple-git": "^3.27.0",
|
||||||
"smart-embed-model": "^1.0.7",
|
"smart-embed-model": "^1.0.7",
|
||||||
|
|||||||
24
pnpm-lock.yaml
generated
24
pnpm-lock.yaml
generated
@ -210,6 +210,9 @@ importers:
|
|||||||
remark-gfm:
|
remark-gfm:
|
||||||
specifier: ^4.0.0
|
specifier: ^4.0.0
|
||||||
version: 4.0.1
|
version: 4.0.1
|
||||||
|
remove-markdown:
|
||||||
|
specifier: ^0.6.2
|
||||||
|
version: 0.6.2
|
||||||
shell-env:
|
shell-env:
|
||||||
specifier: ^4.0.1
|
specifier: ^4.0.1
|
||||||
version: 4.0.1
|
version: 4.0.1
|
||||||
@ -577,8 +580,8 @@ packages:
|
|||||||
'@codemirror/language@6.11.2':
|
'@codemirror/language@6.11.2':
|
||||||
resolution: {integrity: sha512-p44TsNArL4IVXDTbapUmEkAlvWs2CFQbcfc0ymDsis1kH2wh0gcY96AS29c/vp2d0y2Tquk1EDSaawpzilUiAw==}
|
resolution: {integrity: sha512-p44TsNArL4IVXDTbapUmEkAlvWs2CFQbcfc0ymDsis1kH2wh0gcY96AS29c/vp2d0y2Tquk1EDSaawpzilUiAw==}
|
||||||
|
|
||||||
'@codemirror/language@https://codeload.github.com/lishid/cm-language/tar.gz/6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67':
|
'@codemirror/language@git+https://git@github.com:lishid/cm-language.git#6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67':
|
||||||
resolution: {tarball: https://codeload.github.com/lishid/cm-language/tar.gz/6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67}
|
resolution: {commit: 6c1c5f5b677f6f6503d1ca2ec47f62f6406cda67, repo: git@github.com:lishid/cm-language.git, type: git}
|
||||||
version: 6.10.8
|
version: 6.10.8
|
||||||
|
|
||||||
'@codemirror/lint@0.20.3':
|
'@codemirror/lint@0.20.3':
|
||||||
@ -5451,8 +5454,8 @@ packages:
|
|||||||
'@codemirror/state': ^6.0.0
|
'@codemirror/state': ^6.0.0
|
||||||
'@codemirror/view': ^6.0.0
|
'@codemirror/view': ^6.0.0
|
||||||
|
|
||||||
obsidian@https://codeload.github.com/obsidianmd/obsidian-api/tar.gz/103ff76a0712a02dd0da28646b5a6b0ba6686d66:
|
obsidian@git+https://git@github.com:obsidianmd/obsidian-api.git#103ff76a0712a02dd0da28646b5a6b0ba6686d66:
|
||||||
resolution: {tarball: https://codeload.github.com/obsidianmd/obsidian-api/tar.gz/103ff76a0712a02dd0da28646b5a6b0ba6686d66}
|
resolution: {commit: 103ff76a0712a02dd0da28646b5a6b0ba6686d66, repo: git@github.com:obsidianmd/obsidian-api.git, type: git}
|
||||||
version: 1.8.7
|
version: 1.8.7
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@codemirror/state': ^6.0.0
|
'@codemirror/state': ^6.0.0
|
||||||
@ -5921,6 +5924,9 @@ packages:
|
|||||||
remove-markdown@0.5.5:
|
remove-markdown@0.5.5:
|
||||||
resolution: {integrity: sha512-lMR8tOtDqazFT6W2bZidoXwkptMdF3pCxpri0AEokHg0sZlC2GdoLqnoaxsEj1o7/BtXV1MKtT3YviA1t7rW7g==}
|
resolution: {integrity: sha512-lMR8tOtDqazFT6W2bZidoXwkptMdF3pCxpri0AEokHg0sZlC2GdoLqnoaxsEj1o7/BtXV1MKtT3YviA1t7rW7g==}
|
||||||
|
|
||||||
|
remove-markdown@0.6.2:
|
||||||
|
resolution: {integrity: sha512-EijDXJZbzpGbQBd852ViUzcqgpMujthM+SAEHiWCMcZonRbZ+xViWKLJA/vrwbDwYdxrs1aFDjpBhcGrZoJRGA==}
|
||||||
|
|
||||||
require-directory@2.1.1:
|
require-directory@2.1.1:
|
||||||
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -7083,7 +7089,7 @@ snapshots:
|
|||||||
'@lezer/lr': 1.4.2
|
'@lezer/lr': 1.4.2
|
||||||
style-mod: 4.1.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:
|
dependencies:
|
||||||
'@codemirror/state': 6.5.2
|
'@codemirror/state': 6.5.2
|
||||||
'@codemirror/view': 6.38.0
|
'@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):
|
obsidian-daily-notes-interface@0.8.4(@codemirror/state@6.5.2)(@codemirror/view@6.38.0):
|
||||||
dependencies:
|
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
|
tslib: 2.1.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@codemirror/state'
|
- '@codemirror/state'
|
||||||
@ -12606,7 +12612,7 @@ snapshots:
|
|||||||
|
|
||||||
obsidian-dataview@0.5.68:
|
obsidian-dataview@0.5.68:
|
||||||
dependencies:
|
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/state': 6.5.2
|
||||||
'@codemirror/view': 6.38.0
|
'@codemirror/view': 6.38.0
|
||||||
emoji-regex: 10.4.0
|
emoji-regex: 10.4.0
|
||||||
@ -12625,7 +12631,7 @@ snapshots:
|
|||||||
'@types/codemirror': 5.60.8
|
'@types/codemirror': 5.60.8
|
||||||
moment: 2.29.4
|
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:
|
dependencies:
|
||||||
'@codemirror/state': 6.5.2
|
'@codemirror/state': 6.5.2
|
||||||
'@codemirror/view': 6.38.0
|
'@codemirror/view': 6.38.0
|
||||||
@ -13233,6 +13239,8 @@ snapshots:
|
|||||||
|
|
||||||
remove-markdown@0.5.5: {}
|
remove-markdown@0.5.5: {}
|
||||||
|
|
||||||
|
remove-markdown@0.6.2: {}
|
||||||
|
|
||||||
require-directory@2.1.1: {}
|
require-directory@2.1.1: {}
|
||||||
|
|
||||||
require-from-string@2.0.2: {}
|
require-from-string@2.0.2: {}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import { useTrans } from '../../contexts/TransContext'
|
|||||||
import { Workspace } from '../../database/json/workspace/types'
|
import { Workspace } from '../../database/json/workspace/types'
|
||||||
import { WorkspaceManager } from '../../database/json/workspace/WorkspaceManager'
|
import { WorkspaceManager } from '../../database/json/workspace/WorkspaceManager'
|
||||||
import { SelectVector } from '../../database/schema'
|
import { SelectVector } from '../../database/schema'
|
||||||
|
import { t } from '../../lang/helpers'
|
||||||
import { Mentionable } from '../../types/mentionable'
|
import { Mentionable } from '../../types/mentionable'
|
||||||
import { getFilesWithTag } from '../../utils/glob-utils'
|
import { getFilesWithTag } from '../../utils/glob-utils'
|
||||||
import { openMarkdownFile } from '../../utils/obsidian'
|
import { openMarkdownFile } from '../../utils/obsidian'
|
||||||
@ -459,7 +460,7 @@ const SearchView = () => {
|
|||||||
h5: ({ children }) => <h5>{children}</h5>,
|
h5: ({ children }) => <h5>{children}</h5>,
|
||||||
h6: ({ children }) => <h5>{children}</h5>,
|
h6: ({ children }) => <h5>{children}</h5>,
|
||||||
// 移除图片显示,避免布局问题
|
// 移除图片显示,避免布局问题
|
||||||
img: () => <span className="obsidian-image-placeholder">[图片]</span>,
|
img: () => <span className="obsidian-image-placeholder">{t('semanticSearch.imagePlaceholder')}</span>,
|
||||||
// 代码块样式
|
// 代码块样式
|
||||||
code: ({ children, inline }: { children: React.ReactNode; inline?: boolean;[key: string]: unknown }) => {
|
code: ({ children, inline }: { children: React.ReactNode; inline?: boolean;[key: string]: unknown }) => {
|
||||||
if (inline) {
|
if (inline) {
|
||||||
@ -619,7 +620,7 @@ const SearchView = () => {
|
|||||||
{/* 头部信息 */}
|
{/* 头部信息 */}
|
||||||
<div className="obsidian-search-header-wrapper">
|
<div className="obsidian-search-header-wrapper">
|
||||||
<div className="obsidian-search-title">
|
<div className="obsidian-search-title">
|
||||||
<h3>语义索引</h3>
|
<h3>{t('semanticSearch.title')}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 统计信息 */}
|
{/* 统计信息 */}
|
||||||
@ -628,20 +629,20 @@ const SearchView = () => {
|
|||||||
<div className="obsidian-search-stats-overview">
|
<div className="obsidian-search-stats-overview">
|
||||||
<div className="obsidian-search-stats-main">
|
<div className="obsidian-search-stats-main">
|
||||||
<span className="obsidian-search-stats-number">{statisticsInfo.totalChunks}</span>
|
<span className="obsidian-search-stats-number">{statisticsInfo.totalChunks}</span>
|
||||||
<span className="obsidian-search-stats-label">个向量块</span>
|
<span className="obsidian-search-stats-label">{t('semanticSearch.vectorBlocks')}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="obsidian-search-stats-breakdown">
|
<div className="obsidian-search-stats-breakdown">
|
||||||
<div className="obsidian-search-stats-item">
|
<div className="obsidian-search-stats-item">
|
||||||
<span className="obsidian-search-stats-item-icon">📄</span>
|
<span className="obsidian-search-stats-item-icon">📄</span>
|
||||||
<span className="obsidian-search-stats-item-value">{statisticsInfo.totalFiles}</span>
|
<span className="obsidian-search-stats-item-value">{statisticsInfo.totalFiles}</span>
|
||||||
<span className="obsidian-search-stats-item-label">文件</span>
|
<span className="obsidian-search-stats-item-label">{t('semanticSearch.files')}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className="infio-search-model-info">
|
<div className="infio-search-model-info">
|
||||||
<div className="infio-search-model-row">
|
<div className="infio-search-model-row">
|
||||||
<span className="infio-search-model-label">嵌入模型:</span>
|
<span className="infio-search-model-label">{t('semanticSearch.embeddingModel')}</span>
|
||||||
<ModelSelect modelType="embedding" />
|
<ModelSelect modelType="embedding" />
|
||||||
</div>
|
</div>
|
||||||
<div className="obsidian-search-actions">
|
<div className="obsidian-search-actions">
|
||||||
@ -649,9 +650,9 @@ const SearchView = () => {
|
|||||||
onClick={handleInitWorkspaceRAG}
|
onClick={handleInitWorkspaceRAG}
|
||||||
disabled={isInitializingRAG || isDeleting || isSearching}
|
disabled={isInitializingRAG || isDeleting || isSearching}
|
||||||
className="obsidian-search-init-btn"
|
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'))}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -662,15 +663,15 @@ const SearchView = () => {
|
|||||||
{isInitializingRAG && (
|
{isInitializingRAG && (
|
||||||
<div className="obsidian-rag-initializing">
|
<div className="obsidian-rag-initializing">
|
||||||
<div className="obsidian-rag-init-header">
|
<div className="obsidian-rag-init-header">
|
||||||
<h4>正在初始化工作区 RAG 向量索引</h4>
|
<h4>{t('semanticSearch.initializingWorkspace')}</h4>
|
||||||
<p>为当前工作区的文件建立向量索引,提高搜索精度</p>
|
<p>{t('semanticSearch.initializingDescription')}</p>
|
||||||
</div>
|
</div>
|
||||||
{ragInitProgress && ragInitProgress.type === 'indexing' && ragInitProgress.indexProgress && (
|
{ragInitProgress && ragInitProgress.type === 'indexing' && ragInitProgress.indexProgress && (
|
||||||
<div className="obsidian-rag-progress">
|
<div className="obsidian-rag-progress">
|
||||||
<div className="obsidian-rag-progress-info">
|
<div className="obsidian-rag-progress-info">
|
||||||
<span className="obsidian-rag-progress-stage">建立向量索引</span>
|
<span className="obsidian-rag-progress-stage">{t('semanticSearch.buildingVectorIndex')}</span>
|
||||||
<span className="obsidian-rag-progress-counter">
|
<span className="obsidian-rag-progress-counter">
|
||||||
{ragInitProgress.indexProgress.completedChunks} / {ragInitProgress.indexProgress.totalChunks} 块
|
{ragInitProgress.indexProgress.completedChunks} / {ragInitProgress.indexProgress.totalChunks} {t('semanticSearch.blocks')}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="obsidian-rag-progress-bar">
|
<div className="obsidian-rag-progress-bar">
|
||||||
@ -683,7 +684,7 @@ const SearchView = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="obsidian-rag-progress-details">
|
<div className="obsidian-rag-progress-details">
|
||||||
<div className="obsidian-rag-progress-files">
|
<div className="obsidian-rag-progress-files">
|
||||||
共 {ragInitProgress.indexProgress.totalFiles} 个文件
|
{t('semanticSearch.totalFiles', { count: ragInitProgress.indexProgress.totalFiles })}
|
||||||
</div>
|
</div>
|
||||||
<div className="obsidian-rag-progress-percentage">
|
<div className="obsidian-rag-progress-percentage">
|
||||||
{Math.round((ragInitProgress.indexProgress.completedChunks / Math.max(ragInitProgress.indexProgress.totalChunks, 1)) * 100)}%
|
{Math.round((ragInitProgress.indexProgress.completedChunks / Math.max(ragInitProgress.indexProgress.totalChunks, 1)) * 100)}%
|
||||||
@ -701,7 +702,7 @@ const SearchView = () => {
|
|||||||
<span className="obsidian-rag-success-icon">✅</span>
|
<span className="obsidian-rag-success-icon">✅</span>
|
||||||
<div className="obsidian-rag-success-text">
|
<div className="obsidian-rag-success-text">
|
||||||
<span className="obsidian-rag-success-title">
|
<span className="obsidian-rag-success-title">
|
||||||
工作区 RAG 向量索引初始化完成: {ragInitSuccess.workspaceName}
|
{t('semanticSearch.initializationComplete', { workspaceName: ragInitSuccess.workspaceName })}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@ -723,7 +724,7 @@ const SearchView = () => {
|
|||||||
onSubmit={handleSearch}
|
onSubmit={handleSearch}
|
||||||
mentionables={mentionables}
|
mentionables={mentionables}
|
||||||
setMentionables={setMentionables}
|
setMentionables={setMentionables}
|
||||||
placeholder="语义搜索(按回车键搜索)..."
|
placeholder={t('semanticSearch.searchPlaceholder')}
|
||||||
autoFocus={true}
|
autoFocus={true}
|
||||||
disabled={isSearching}
|
disabled={isSearching}
|
||||||
searchMode={searchMode}
|
searchMode={searchMode}
|
||||||
@ -737,11 +738,11 @@ const SearchView = () => {
|
|||||||
<div className="obsidian-search-stats">
|
<div className="obsidian-search-stats">
|
||||||
<div className="obsidian-search-stats-line">
|
<div className="obsidian-search-stats-line">
|
||||||
{searchMode === 'notes' ? (
|
{searchMode === 'notes' ? (
|
||||||
`${totalFiles} 个文件,${totalBlocks} 个块`
|
t('semanticSearch.stats.filesAndBlocks', { files: totalFiles, blocks: totalBlocks })
|
||||||
) : searchMode === 'insights' ? (
|
) : searchMode === 'insights' ? (
|
||||||
`${insightGroupedResults.length} 个文件,${insightResults.length} 个洞察`
|
t('semanticSearch.stats.filesAndInsights', { files: insightGroupedResults.length, insights: insightResults.length })
|
||||||
) : (
|
) : (
|
||||||
`${totalAllFiles} 个文件,${totalBlocks} 个块,${insightResults.length} 个洞察`
|
t('semanticSearch.stats.filesBlocksAndInsights', { files: totalAllFiles, blocks: totalBlocks, insights: insightResults.length })
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -751,17 +752,17 @@ const SearchView = () => {
|
|||||||
<div className="obsidian-confirm-dialog-overlay">
|
<div className="obsidian-confirm-dialog-overlay">
|
||||||
<div className="obsidian-confirm-dialog">
|
<div className="obsidian-confirm-dialog">
|
||||||
<div className="obsidian-confirm-dialog-header">
|
<div className="obsidian-confirm-dialog-header">
|
||||||
<h3>清除工作区索引</h3>
|
<h3>{t('semanticSearch.deleteConfirm.title')}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="obsidian-confirm-dialog-body">
|
<div className="obsidian-confirm-dialog-body">
|
||||||
<p>
|
<p>
|
||||||
将清除当前工作区的所有向量索引数据。
|
{t('semanticSearch.deleteConfirm.message')}
|
||||||
</p>
|
</p>
|
||||||
<p className="obsidian-confirm-dialog-warning">
|
<p className="obsidian-confirm-dialog-warning">
|
||||||
此操作无法撤销,清除后需要重新初始化索引才能进行语义搜索。
|
{t('semanticSearch.deleteConfirm.warning')}
|
||||||
</p>
|
</p>
|
||||||
<div className="obsidian-confirm-dialog-scope">
|
<div className="obsidian-confirm-dialog-scope">
|
||||||
<strong>工作区:</strong> {settings.workspace === 'vault' ? '整个 Vault' : settings.workspace}
|
<strong>{t('semanticSearch.deleteConfirm.workspaceLabel')}</strong> {settings.workspace === 'vault' ? t('semanticSearch.deleteConfirm.entireVault') : settings.workspace}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="obsidian-confirm-dialog-footer">
|
<div className="obsidian-confirm-dialog-footer">
|
||||||
@ -769,13 +770,13 @@ const SearchView = () => {
|
|||||||
onClick={cancelDeleteConfirm}
|
onClick={cancelDeleteConfirm}
|
||||||
className="obsidian-confirm-dialog-cancel-btn"
|
className="obsidian-confirm-dialog-cancel-btn"
|
||||||
>
|
>
|
||||||
取消
|
{t('semanticSearch.deleteConfirm.cancel')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={confirmDeleteWorkspaceIndex}
|
onClick={confirmDeleteWorkspaceIndex}
|
||||||
className="obsidian-confirm-dialog-confirm-btn"
|
className="obsidian-confirm-dialog-confirm-btn"
|
||||||
>
|
>
|
||||||
确认清除
|
{t('semanticSearch.deleteConfirm.confirm')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -787,31 +788,31 @@ const SearchView = () => {
|
|||||||
<div className="obsidian-confirm-dialog-overlay">
|
<div className="obsidian-confirm-dialog-overlay">
|
||||||
<div className="obsidian-confirm-dialog">
|
<div className="obsidian-confirm-dialog">
|
||||||
<div className="obsidian-confirm-dialog-header">
|
<div className="obsidian-confirm-dialog-header">
|
||||||
<h3>{statisticsInfo && (statisticsInfo.totalFiles > 0 || statisticsInfo.totalChunks > 0) ? '更新工作区索引' : '初始化工作区索引'}</h3>
|
<h3>{statisticsInfo && (statisticsInfo.totalFiles > 0 || statisticsInfo.totalChunks > 0) ? t('semanticSearch.initConfirm.updateTitle') : t('semanticSearch.initConfirm.initTitle')}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div className="obsidian-confirm-dialog-body">
|
<div className="obsidian-confirm-dialog-body">
|
||||||
<p>
|
<p>
|
||||||
{statisticsInfo && (statisticsInfo.totalFiles > 0 || statisticsInfo.totalChunks > 0)
|
{statisticsInfo && (statisticsInfo.totalFiles > 0 || statisticsInfo.totalChunks > 0)
|
||||||
? '将更新当前工作区的向量索引,重新处理所有文件以确保索引最新。'
|
? t('semanticSearch.initConfirm.updateMessage')
|
||||||
: '将为当前工作区的所有文件建立向量索引,这将提高语义搜索的准确性。'
|
: t('semanticSearch.initConfirm.initMessage')
|
||||||
}
|
}
|
||||||
</p>
|
</p>
|
||||||
<div className="obsidian-confirm-dialog-info">
|
<div className="obsidian-confirm-dialog-info">
|
||||||
<div className="obsidian-confirm-dialog-info-item">
|
<div className="obsidian-confirm-dialog-info-item">
|
||||||
<strong>嵌入模型:</strong>
|
<strong>{t('semanticSearch.initConfirm.embeddingModelLabel')}</strong>
|
||||||
<span className="obsidian-confirm-dialog-model">
|
<span className="obsidian-confirm-dialog-model">
|
||||||
{settings.embeddingModelId}
|
{settings.embeddingModelId}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="obsidian-confirm-dialog-info-item">
|
<div className="obsidian-confirm-dialog-info-item">
|
||||||
<strong>工作区:</strong>
|
<strong>{t('semanticSearch.initConfirm.workspaceLabel')}</strong>
|
||||||
<span className="obsidian-confirm-dialog-workspace">
|
<span className="obsidian-confirm-dialog-workspace">
|
||||||
{settings.workspace === 'vault' ? '整个 Vault' : settings.workspace}
|
{settings.workspace === 'vault' ? t('semanticSearch.initConfirm.entireVault') : settings.workspace}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="obsidian-confirm-dialog-warning">
|
<p className="obsidian-confirm-dialog-warning">
|
||||||
此操作可能需要几分钟时间,具体取决于文件数量和大小。
|
{t('semanticSearch.initConfirm.warning')}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="obsidian-confirm-dialog-footer">
|
<div className="obsidian-confirm-dialog-footer">
|
||||||
@ -819,13 +820,13 @@ const SearchView = () => {
|
|||||||
onClick={cancelRAGInitConfirm}
|
onClick={cancelRAGInitConfirm}
|
||||||
className="obsidian-confirm-dialog-cancel-btn"
|
className="obsidian-confirm-dialog-cancel-btn"
|
||||||
>
|
>
|
||||||
取消
|
{t('semanticSearch.initConfirm.cancel')}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={confirmInitWorkspaceRAG}
|
onClick={confirmInitWorkspaceRAG}
|
||||||
className="obsidian-confirm-dialog-confirm-btn"
|
className="obsidian-confirm-dialog-confirm-btn"
|
||||||
>
|
>
|
||||||
{statisticsInfo && (statisticsInfo.totalFiles > 0 || statisticsInfo.totalChunks > 0) ? '开始更新' : '开始初始化'}
|
{statisticsInfo && (statisticsInfo.totalFiles > 0 || statisticsInfo.totalChunks > 0) ? t('semanticSearch.initConfirm.startUpdate') : t('semanticSearch.initConfirm.startInit')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -835,7 +836,7 @@ const SearchView = () => {
|
|||||||
{/* 搜索进度 */}
|
{/* 搜索进度 */}
|
||||||
{isSearching && (
|
{isSearching && (
|
||||||
<div className="obsidian-search-loading">
|
<div className="obsidian-search-loading">
|
||||||
正在搜索...
|
{t('semanticSearch.searching')}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -1044,7 +1045,7 @@ const SearchView = () => {
|
|||||||
(searchMode === 'all' && allGroupedResults.length === 0)
|
(searchMode === 'all' && allGroupedResults.length === 0)
|
||||||
) && (
|
) && (
|
||||||
<div className="obsidian-no-results">
|
<div className="obsidian-no-results">
|
||||||
<p>未找到相关结果</p>
|
<p>{t('semanticSearch.noResults')}</p>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -2,6 +2,8 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
|||||||
import { ChevronDown, ChevronUp, FileText, Lightbulb, Globe } from 'lucide-react'
|
import { ChevronDown, ChevronUp, FileText, Lightbulb, Globe } from 'lucide-react'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
import { t } from '../../../lang/helpers'
|
||||||
|
|
||||||
interface SearchModeSelectProps {
|
interface SearchModeSelectProps {
|
||||||
searchMode: 'notes' | 'insights' | 'all'
|
searchMode: 'notes' | 'insights' | 'all'
|
||||||
onSearchModeChange: (mode: 'notes' | 'insights' | 'all') => void
|
onSearchModeChange: (mode: 'notes' | 'insights' | 'all') => void
|
||||||
@ -13,21 +15,21 @@ export function SearchModeSelect({ searchMode, onSearchModeChange }: SearchModeS
|
|||||||
const searchModes = [
|
const searchModes = [
|
||||||
{
|
{
|
||||||
value: 'all' as const,
|
value: 'all' as const,
|
||||||
name: '全部',
|
name: t('semanticSearch.searchMode.all'),
|
||||||
icon: <Globe size={14} />,
|
icon: <Globe size={14} />,
|
||||||
description: '聚合搜索原始笔记和 AI 洞察'
|
description: t('semanticSearch.searchMode.allDescription')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'notes' as const,
|
value: 'notes' as const,
|
||||||
name: '原始笔记',
|
name: t('semanticSearch.searchMode.notes'),
|
||||||
icon: <FileText size={14} />,
|
icon: <FileText size={14} />,
|
||||||
description: '搜索原始笔记内容'
|
description: t('semanticSearch.searchMode.notesDescription')
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'insights' as const,
|
value: 'insights' as const,
|
||||||
name: 'AI 洞察',
|
name: t('semanticSearch.searchMode.insights'),
|
||||||
icon: <Lightbulb size={14} />,
|
icon: <Lightbulb size={14} />,
|
||||||
description: '搜索 AI 洞察内容'
|
description: t('semanticSearch.searchMode.insightsDescription')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
import { backOff } from 'exponential-backoff';
|
import { backOff } from 'exponential-backoff';
|
||||||
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
|
import { RecursiveCharacterTextSplitter } from 'langchain/text_splitter';
|
||||||
import removeMarkdown from "markdown-to-text";
|
|
||||||
import { minimatch } from 'minimatch';
|
import { minimatch } from 'minimatch';
|
||||||
import { App, Notice, TFile } from 'obsidian';
|
import { App, Notice, TFile } from 'obsidian';
|
||||||
import pLimit from 'p-limit';
|
import pLimit from 'p-limit';
|
||||||
|
import removeMarkdown from 'remove-markdown';
|
||||||
|
|
||||||
import { IndexProgress } from '../../../components/chat-view/QueryProgress';
|
import { IndexProgress } from '../../../components/chat-view/QueryProgress';
|
||||||
import {
|
import {
|
||||||
@ -14,10 +14,10 @@ import {
|
|||||||
} from '../../../core/llm/exception';
|
} from '../../../core/llm/exception';
|
||||||
import { InsertVector, SelectVector } from '../../../database/schema';
|
import { InsertVector, SelectVector } from '../../../database/schema';
|
||||||
import { EmbeddingModel } from '../../../types/embedding';
|
import { EmbeddingModel } from '../../../types/embedding';
|
||||||
import { openSettingsModalWithError } from '../../../utils/open-settings-modal';
|
|
||||||
import { getFilesWithTag } from '../../../utils/glob-utils';
|
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 { DBManager } from '../../database-manager';
|
||||||
|
import { Workspace } from '../../json/workspace/types';
|
||||||
|
|
||||||
import { VectorRepository } from './vector-repository';
|
import { VectorRepository } from './vector-repository';
|
||||||
|
|
||||||
@ -256,7 +256,7 @@ export class VectorManager {
|
|||||||
async () => {
|
async () => {
|
||||||
// 在嵌入之前处理 markdown
|
// 在嵌入之前处理 markdown
|
||||||
const cleanedBatchData = batchChunks.map(chunk => {
|
const cleanedBatchData = batchChunks.map(chunk => {
|
||||||
const cleanContent = removeMarkdown(chunk.content).replace(/\0/g, '')
|
const cleanContent = removeMarkdown(chunk.content)
|
||||||
return { chunk, cleanContent }
|
return { chunk, cleanContent }
|
||||||
}).filter(({ cleanContent }) => cleanContent && cleanContent.trim().length > 0)
|
}).filter(({ cleanContent }) => cleanContent && cleanContent.trim().length > 0)
|
||||||
|
|
||||||
@ -539,7 +539,7 @@ export class VectorManager {
|
|||||||
async () => {
|
async () => {
|
||||||
// 在嵌入之前处理 markdown,只处理一次
|
// 在嵌入之前处理 markdown,只处理一次
|
||||||
const cleanedBatchData = batchChunks.map(chunk => {
|
const cleanedBatchData = batchChunks.map(chunk => {
|
||||||
const cleanContent = removeMarkdown(chunk.content).replace(/\0/g, '')
|
const cleanContent = removeMarkdown(chunk.content)
|
||||||
return { chunk, cleanContent }
|
return { chunk, cleanContent }
|
||||||
}).filter(({ cleanContent }) => cleanContent && cleanContent.trim().length > 0)
|
}).filter(({ cleanContent }) => cleanContent && cleanContent.trim().length > 0)
|
||||||
|
|
||||||
|
|||||||
@ -497,6 +497,63 @@ export default {
|
|||||||
toolNoDescription: "No description",
|
toolNoDescription: "No description",
|
||||||
useMcpToolFrom: "Use MCP tool from",
|
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: {
|
insights: {
|
||||||
title: "AI Insights",
|
title: "AI Insights",
|
||||||
initializeInsights: "Initialize Insights",
|
initializeInsights: "Initialize Insights",
|
||||||
|
|||||||
@ -499,6 +499,63 @@ export default {
|
|||||||
useMcpToolFrom: "使用来自以下的 MCP 工具:",
|
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: {
|
insights: {
|
||||||
title: "AI 洞察",
|
title: "AI 洞察",
|
||||||
initializeInsights: "初始化洞察",
|
initializeInsights: "初始化洞察",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user