- Introduces a new match_search_files tool for fuzzy/keyword search, integrating with Obsidian's core search plugin and updating Omnisearch integration for improved file search capabilities. - Adds settings for selecting search backends (core plugin, Omnisearch, ripgrep) for both regex and match searches. - Updates language files, prompts, and types to support the new functionality. - Restructures search-related files for better organization.
753 lines
22 KiB
TypeScript
753 lines
22 KiB
TypeScript
import {
|
|
App,
|
|
Modal,
|
|
Notice,
|
|
PluginSettingTab,
|
|
Setting,
|
|
TFile
|
|
} from 'obsidian';
|
|
import * as React from "react";
|
|
import { createRoot } from "react-dom/client";
|
|
|
|
import { t } from '../lang/helpers';
|
|
import InfioPlugin from '../main';
|
|
import { InfioSettings } from '../types/settings';
|
|
import { findFilesMatchingPatterns } from '../utils/glob-utils';
|
|
|
|
// import AdvancedSettings from './components/AdvancedSettings';
|
|
import BasicAutoCompleteSettings from './components/BasicAutoCompleteSettings';
|
|
// import DangerZoneSettings from './components/DangerZoneSettings';
|
|
import CustomProviderSettings from './components/ModelProviderSettings';
|
|
import PostprocessingSettings from './components/PostprocessingSettings';
|
|
import PreprocessingSettings from './components/PreprocessingSettings';
|
|
import PrivacySettings from './components/PrivacySettings';
|
|
import TriggerSettingsSection from './components/TriggerSettingsSection';
|
|
|
|
export class InfioSettingTab extends PluginSettingTab {
|
|
plugin: InfioPlugin;
|
|
private autoCompleteContainer: HTMLElement | null = null;
|
|
private modelsContainer: HTMLElement | null = null;
|
|
|
|
constructor(app: App, plugin: InfioPlugin) {
|
|
super(app, plugin)
|
|
this.plugin = plugin
|
|
}
|
|
|
|
display(): void {
|
|
const { containerEl } = this
|
|
containerEl.empty()
|
|
this.renderModelsSection(containerEl)
|
|
this.renderModelParametersSection(containerEl)
|
|
this.renderFilesSearchSection(containerEl)
|
|
this.renderChatBehaviorSection(containerEl)
|
|
this.renderDeepResearchSection(containerEl)
|
|
this.renderRAGSection(containerEl)
|
|
this.renderAutoCompleteSection(containerEl)
|
|
}
|
|
|
|
private renderModelsContent(containerEl: HTMLElement): void {
|
|
const div = containerEl.createDiv("div");
|
|
const sections = createRoot(div);
|
|
sections.render(
|
|
<CustomProviderSettings
|
|
plugin={this.plugin}
|
|
onSettingsUpdate={() => {
|
|
if (this.modelsContainer) {
|
|
this.modelsContainer.empty();
|
|
this.renderModelsContent(this.modelsContainer);
|
|
}
|
|
}}
|
|
/>
|
|
);
|
|
}
|
|
|
|
private renderModelParametersSection(containerEl: HTMLElement): void {
|
|
new Setting(containerEl).setHeading().setName(t('settings.ModelParameters.title'));
|
|
new Setting(containerEl)
|
|
.setName(t('settings.ModelParameters.temperature'))
|
|
.setDesc(t('settings.ModelParameters.temperatureDescription'))
|
|
.addText((text) => {
|
|
text
|
|
.setValue(String(this.plugin.settings.modelOptions.temperature))
|
|
.onChange(async (value) => {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
modelOptions: {
|
|
...this.plugin.settings.modelOptions,
|
|
temperature: parseFloat(value),
|
|
},
|
|
});
|
|
})
|
|
});
|
|
new Setting(containerEl)
|
|
.setName(t('settings.ModelParameters.topP'))
|
|
.setDesc(t('settings.ModelParameters.topPDescription'))
|
|
.addText((text) => {
|
|
text
|
|
.setValue(String(this.plugin.settings.modelOptions.top_p))
|
|
.onChange(async (value) => {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
modelOptions: {
|
|
...this.plugin.settings.modelOptions,
|
|
top_p: parseFloat(value),
|
|
},
|
|
});
|
|
})
|
|
});
|
|
new Setting(containerEl)
|
|
.setName(t('settings.ModelParameters.frequencyPenalty'))
|
|
.setDesc(t('settings.ModelParameters.frequencyPenaltyDescription'))
|
|
.addText((text) => {
|
|
text
|
|
.setValue(String(this.plugin.settings.modelOptions.frequency_penalty))
|
|
.onChange(async (value) => {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
modelOptions: {
|
|
...this.plugin.settings.modelOptions,
|
|
frequency_penalty: parseFloat(value),
|
|
},
|
|
});
|
|
})
|
|
});
|
|
new Setting(containerEl)
|
|
.setName(t('settings.ModelParameters.presencePenalty'))
|
|
.setDesc(t('settings.ModelParameters.presencePenaltyDescription'))
|
|
.addText((text) => {
|
|
text
|
|
.setValue(String(this.plugin.settings.modelOptions.presence_penalty))
|
|
.onChange(async (value) => {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
modelOptions: {
|
|
...this.plugin.settings.modelOptions,
|
|
presence_penalty: parseFloat(value),
|
|
},
|
|
});
|
|
})
|
|
});
|
|
new Setting(containerEl)
|
|
.setName(t('settings.ModelParameters.maxTokens'))
|
|
.setDesc(t('settings.ModelParameters.maxTokensDescription'))
|
|
.addText((text) => {
|
|
text
|
|
.setValue(String(this.plugin.settings.modelOptions.max_tokens))
|
|
.onChange(async (value) => {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
modelOptions: {
|
|
...this.plugin.settings.modelOptions,
|
|
max_tokens: parseInt(value),
|
|
},
|
|
});
|
|
})
|
|
});
|
|
}
|
|
|
|
private renderFilesSearchSection(containerEl: HTMLElement): void {
|
|
new Setting(containerEl).setHeading().setName(t('settings.FilesSearch.title'))
|
|
new Setting(containerEl)
|
|
.setName(t('settings.FilesSearch.method'))
|
|
.setDesc(t('settings.FilesSearch.methodDescription'))
|
|
.addDropdown((dropdown) =>
|
|
dropdown
|
|
.addOption('auto', t('settings.FilesSearch.auto'))
|
|
.addOption('semantic', t('settings.FilesSearch.semantic'))
|
|
.addOption('regex', t('settings.FilesSearch.regex'))
|
|
.addOption('match', t('settings.FilesSearch.match'))
|
|
.setValue(this.plugin.settings.filesSearchMethod)
|
|
.onChange(async (value) => {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
filesSearchMethod: value as 'match' | 'regex' | 'semantic' | 'auto',
|
|
})
|
|
}),
|
|
)
|
|
new Setting(containerEl)
|
|
.setName(t('settings.FilesSearch.regexBackend'))
|
|
.setDesc(t('settings.FilesSearch.regexBackendDescription'))
|
|
.addDropdown((dropdown) =>
|
|
dropdown
|
|
.addOption('ripgrep', t('settings.FilesSearch.ripgrep'))
|
|
.addOption('coreplugin', t('settings.FilesSearch.coreplugin'))
|
|
.setValue(this.plugin.settings.regexSearchBackend)
|
|
.onChange(async (value) => {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
regexSearchBackend: value as 'ripgrep' | 'coreplugin',
|
|
})
|
|
}),
|
|
)
|
|
new Setting(containerEl)
|
|
.setName(t('settings.FilesSearch.matchBackend'))
|
|
.setDesc(t('settings.FilesSearch.matchBackendDescription'))
|
|
.addDropdown((dropdown) =>
|
|
dropdown
|
|
.addOption('coreplugin', t('settings.FilesSearch.coreplugin'))
|
|
.addOption('omnisearch', t('settings.FilesSearch.omnisearch'))
|
|
.setValue(this.plugin.settings.matchSearchBackend)
|
|
.onChange(async (value) => {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
matchSearchBackend: value as 'coreplugin' | 'omnisearch',
|
|
})
|
|
}),
|
|
)
|
|
new Setting(containerEl)
|
|
.setName(t('settings.FilesSearch.ripgrepPath'))
|
|
.setDesc(t('settings.FilesSearch.ripgrepPathDescription'))
|
|
.addText((text) =>
|
|
text
|
|
.setPlaceholder('/opt/homebrew/bin/')
|
|
.setValue(this.plugin.settings.ripgrepPath)
|
|
.onChange(async (value) => {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
ripgrepPath: value,
|
|
})
|
|
}),
|
|
)
|
|
}
|
|
|
|
private renderChatBehaviorSection(containerEl: HTMLElement): void {
|
|
new Setting(containerEl).setHeading().setName(t('settings.ChatBehavior.title'));
|
|
new Setting(containerEl)
|
|
.setName(t('settings.ChatBehavior.defaultMention'))
|
|
.setDesc(t('settings.ChatBehavior.defaultMentionDescription'))
|
|
.addDropdown((dropdown) =>
|
|
dropdown
|
|
.addOption('none', t('settings.ChatBehavior.none'))
|
|
.addOption('current-file', t('settings.ChatBehavior.currentFile'))
|
|
.addOption('vault', t('settings.ChatBehavior.vault'))
|
|
.setValue(this.plugin.settings.defaultMention || 'none')
|
|
.onChange(async (value) => {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
defaultMention: value as 'none' | 'current-file' | 'vault',
|
|
});
|
|
}),
|
|
);
|
|
}
|
|
|
|
renderModelsSection(containerEl: HTMLElement): void {
|
|
const modelsDiv = containerEl.createDiv("models-section");
|
|
this.modelsContainer = modelsDiv;
|
|
this.renderModelsContent(modelsDiv);
|
|
}
|
|
|
|
renderDeepResearchSection(containerEl: HTMLElement): void {
|
|
new Setting(containerEl)
|
|
.setHeading()
|
|
.setName(t('settings.WebSearch.title'))
|
|
|
|
new Setting(containerEl)
|
|
.setName(t('settings.WebSearch.serperApiKey'))
|
|
.setDesc(createFragment(el => {
|
|
el.appendText(t('settings.WebSearch.serperApiKeyDescription') + ' ');
|
|
const a = el.createEl('a', {
|
|
href: 'https://serpapi.com/manage-api-key',
|
|
text: 'https://serpapi.com/manage-api-key'
|
|
});
|
|
a.setAttr('target', '_blank');
|
|
a.setAttr('rel', 'noopener');
|
|
}))
|
|
.setClass('setting-item-heading-smaller')
|
|
.addText((text) => {
|
|
const t = text
|
|
.setValue(this.plugin.settings.serperApiKey)
|
|
.onChange(async (value) => {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
serperApiKey: value,
|
|
})
|
|
});
|
|
if (t.inputEl) {
|
|
t.inputEl.type = "password";
|
|
}
|
|
return t;
|
|
})
|
|
|
|
new Setting(containerEl)
|
|
.setName(t('settings.WebSearch.searchEngine'))
|
|
.setDesc(t('settings.WebSearch.searchEngineDescription'))
|
|
.addDropdown((dropdown) =>
|
|
dropdown
|
|
.addOption('google', t('settings.WebSearch.google'))
|
|
.addOption('duckduckgo', t('settings.WebSearch.duckDuckGo'))
|
|
.addOption('bing', t('settings.WebSearch.bing'))
|
|
.setValue(this.plugin.settings.serperSearchEngine)
|
|
.onChange(async (value) => {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
// @ts-ignore
|
|
serperSearchEngine: value,
|
|
})
|
|
}),
|
|
)
|
|
|
|
new Setting(containerEl)
|
|
.setName(t('settings.WebSearch.jinaApiKey'))
|
|
.setDesc(createFragment(el => {
|
|
el.appendText(t('settings.WebSearch.jinaApiKeyDescription') + ' ');
|
|
const a = el.createEl('a', {
|
|
href: 'https://jina.ai/api-key',
|
|
text: 'https://jina.ai/api-key'
|
|
});
|
|
a.setAttr('target', '_blank');
|
|
a.setAttr('rel', 'noopener');
|
|
}))
|
|
.setClass('setting-item-heading-smaller')
|
|
.addText((text) => {
|
|
const t = text
|
|
.setValue(this.plugin.settings.jinaApiKey)
|
|
.onChange(async (value) => {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
jinaApiKey: value,
|
|
})
|
|
});
|
|
if (t.inputEl) {
|
|
t.inputEl.type = "password";
|
|
}
|
|
return t;
|
|
})
|
|
}
|
|
|
|
renderRAGSection(containerEl: HTMLElement): void {
|
|
// 创建一个折叠区域的容器
|
|
const ragContainer = containerEl.createDiv("rag-settings-container");
|
|
|
|
// 创建标题元素,添加折叠控件
|
|
const headerEl = ragContainer.createEl("div", { cls: "infio-collapsible-heading" });
|
|
|
|
// 添加展开/折叠指示器
|
|
const toggleIcon = headerEl.createEl("span", { cls: "infio-toggle-icon" });
|
|
toggleIcon.textContent = "▶"; // 默认为折叠状态,使用右箭头
|
|
|
|
// 添加标题文本
|
|
const titleEl = headerEl.createEl("h3", { text: t('settings.RAG.title') });
|
|
|
|
// 创建内容容器
|
|
const contentContainer = ragContainer.createEl("div", { cls: "infio-collapsible-content" });
|
|
|
|
// 默认设置为隐藏状态
|
|
contentContainer.style.display = "none";
|
|
|
|
// 添加点击事件处理
|
|
headerEl.addEventListener("click", () => {
|
|
if (contentContainer.style.display === "none") {
|
|
contentContainer.style.display = "block";
|
|
toggleIcon.textContent = "▼"; // 展开状态使用下箭头
|
|
toggleIcon.style.transform = "rotate(0deg)";
|
|
} else {
|
|
contentContainer.style.display = "none";
|
|
toggleIcon.textContent = "▶"; // 折叠状态使用右箭头
|
|
toggleIcon.style.transform = "rotate(0deg)";
|
|
}
|
|
});
|
|
|
|
// 添加样式
|
|
headerEl.style.cursor = "pointer";
|
|
headerEl.style.display = "flex";
|
|
headerEl.style.alignItems = "center";
|
|
headerEl.style.marginBottom = "10px";
|
|
headerEl.style.padding = "6px 0";
|
|
|
|
toggleIcon.style.marginRight = "5px";
|
|
toggleIcon.style.fontSize = "10px";
|
|
toggleIcon.style.transition = "transform 0.15s ease";
|
|
|
|
titleEl.style.margin = "0";
|
|
titleEl.style.fontSize = "16px";
|
|
titleEl.style.fontWeight = "600";
|
|
|
|
// 以下是原有的设置内容,移动到内容容器中
|
|
new Setting(contentContainer)
|
|
.setName(t('settings.RAG.includePatterns'))
|
|
.setDesc(
|
|
t('settings.RAG.includePatternsDescription'),
|
|
)
|
|
.addButton((button) =>
|
|
button.setButtonText(t('settings.RAG.testPatterns')).onClick(async () => {
|
|
const patterns = this.plugin.settings.ragOptions.includePatterns
|
|
const includedFiles = await findFilesMatchingPatterns(
|
|
patterns,
|
|
this.plugin.app.vault,
|
|
)
|
|
new IncludedFilesModal(this.app, includedFiles, patterns).open()
|
|
}),
|
|
)
|
|
new Setting(contentContainer)
|
|
.setClass('infio-chat-settings-textarea')
|
|
.addTextArea((text) =>
|
|
text
|
|
.setValue(this.plugin.settings.ragOptions.includePatterns.join('\n'))
|
|
.onChange(async (value) => {
|
|
const patterns = value
|
|
.split('\n')
|
|
.map((p) => p.trim())
|
|
.filter((p) => p.length > 0)
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
ragOptions: {
|
|
...this.plugin.settings.ragOptions,
|
|
includePatterns: patterns,
|
|
},
|
|
})
|
|
}),
|
|
)
|
|
|
|
new Setting(contentContainer)
|
|
.setName(t('settings.RAG.excludePatterns'))
|
|
.setDesc(
|
|
t('settings.RAG.excludePatternsDescription'),
|
|
)
|
|
.addButton((button) =>
|
|
button.setButtonText(t('settings.RAG.testPatterns')).onClick(async () => {
|
|
const patterns = this.plugin.settings.ragOptions.excludePatterns
|
|
const excludedFiles = await findFilesMatchingPatterns(
|
|
patterns,
|
|
this.plugin.app.vault,
|
|
)
|
|
new ExcludedFilesModal(this.app, excludedFiles).open()
|
|
}),
|
|
)
|
|
new Setting(contentContainer)
|
|
.setClass('infio-chat-settings-textarea')
|
|
.addTextArea((text) =>
|
|
text
|
|
.setValue(this.plugin.settings.ragOptions.excludePatterns.join('\n'))
|
|
.onChange(async (value) => {
|
|
const patterns = value
|
|
.split('\n')
|
|
.map((p) => p.trim())
|
|
.filter((p) => p.length > 0)
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
ragOptions: {
|
|
...this.plugin.settings.ragOptions,
|
|
excludePatterns: patterns,
|
|
},
|
|
})
|
|
}),
|
|
)
|
|
|
|
new Setting(contentContainer)
|
|
.setName(t('settings.RAG.chunkSize'))
|
|
.setDesc(
|
|
t('settings.RAG.chunkSizeDescription'),
|
|
)
|
|
.addText((text) =>
|
|
text
|
|
.setPlaceholder('1000')
|
|
.setValue(String(this.plugin.settings.ragOptions.chunkSize))
|
|
.onChange(async (value) => {
|
|
const chunkSize = parseInt(value, 10)
|
|
if (!isNaN(chunkSize)) {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
ragOptions: {
|
|
...this.plugin.settings.ragOptions,
|
|
chunkSize,
|
|
},
|
|
})
|
|
}
|
|
}),
|
|
)
|
|
|
|
new Setting(contentContainer)
|
|
.setName(t('settings.RAG.thresholdTokens'))
|
|
.setDesc(
|
|
t('settings.RAG.thresholdTokensDescription'),
|
|
)
|
|
.addText((text) =>
|
|
text
|
|
.setPlaceholder('8192')
|
|
.setValue(String(this.plugin.settings.ragOptions.thresholdTokens))
|
|
.onChange(async (value) => {
|
|
const thresholdTokens = parseInt(value, 10)
|
|
if (!isNaN(thresholdTokens)) {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
ragOptions: {
|
|
...this.plugin.settings.ragOptions,
|
|
thresholdTokens,
|
|
},
|
|
})
|
|
}
|
|
}),
|
|
)
|
|
|
|
new Setting(contentContainer)
|
|
.setName(t('settings.RAG.minSimilarity'))
|
|
.setDesc(
|
|
t('settings.RAG.minSimilarityDescription'),
|
|
)
|
|
.addText((text) =>
|
|
text
|
|
.setPlaceholder('0.0')
|
|
.setValue(String(this.plugin.settings.ragOptions.minSimilarity))
|
|
.onChange(async (value) => {
|
|
const minSimilarity = parseFloat(value)
|
|
if (!isNaN(minSimilarity)) {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
ragOptions: {
|
|
...this.plugin.settings.ragOptions,
|
|
minSimilarity,
|
|
},
|
|
})
|
|
}
|
|
}),
|
|
)
|
|
|
|
new Setting(contentContainer)
|
|
.setName(t('settings.RAG.limit'))
|
|
.setDesc(
|
|
t('settings.RAG.limitDescription'),
|
|
)
|
|
.addText((text) =>
|
|
text
|
|
.setPlaceholder('10')
|
|
.setValue(String(this.plugin.settings.ragOptions.limit))
|
|
.onChange(async (value) => {
|
|
const limit = parseInt(value, 10)
|
|
if (!isNaN(limit)) {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
ragOptions: {
|
|
...this.plugin.settings.ragOptions,
|
|
limit,
|
|
},
|
|
})
|
|
}
|
|
}),
|
|
)
|
|
}
|
|
|
|
renderAutoCompleteSection(containerEl: HTMLElement): void {
|
|
// 创建一个折叠区域的容器
|
|
const autoCompleteContainer = containerEl.createDiv("auto-complete-settings-container");
|
|
|
|
// 创建标题元素,添加折叠控件
|
|
const headerEl = autoCompleteContainer.createEl("div", { cls: "infio-collapsible-heading" });
|
|
|
|
// 添加展开/折叠指示器
|
|
const toggleIcon = headerEl.createEl("span", { cls: "infio-toggle-icon" });
|
|
toggleIcon.textContent = "▶"; // 默认为折叠状态,使用右箭头
|
|
|
|
// 添加标题文本
|
|
const titleEl = headerEl.createEl("h3", { text: t('settings.AutoComplete.title') });
|
|
|
|
// 创建内容容器
|
|
const contentContainer = autoCompleteContainer.createEl("div", { cls: "infio-collapsible-content" });
|
|
|
|
// 保存容器引用
|
|
this.autoCompleteContainer = contentContainer;
|
|
|
|
// 默认设置为隐藏状态
|
|
contentContainer.style.display = "none";
|
|
|
|
// 添加点击事件处理
|
|
headerEl.addEventListener("click", () => {
|
|
if (contentContainer.style.display === "none") {
|
|
contentContainer.style.display = "block";
|
|
toggleIcon.textContent = "▼"; // 展开状态使用下箭头
|
|
toggleIcon.style.transform = "rotate(0deg)";
|
|
} else {
|
|
contentContainer.style.display = "none";
|
|
toggleIcon.textContent = "▶"; // 折叠状态使用右箭头
|
|
toggleIcon.style.transform = "rotate(0deg)";
|
|
}
|
|
});
|
|
|
|
// 添加样式
|
|
headerEl.style.cursor = "pointer";
|
|
headerEl.style.display = "flex";
|
|
headerEl.style.alignItems = "center";
|
|
headerEl.style.marginBottom = "10px";
|
|
headerEl.style.padding = "6px 0";
|
|
|
|
toggleIcon.style.marginRight = "5px";
|
|
toggleIcon.style.fontSize = "10px";
|
|
toggleIcon.style.transition = "transform 0.15s ease";
|
|
|
|
titleEl.style.margin = "0";
|
|
titleEl.style.fontSize = "16px";
|
|
titleEl.style.fontWeight = "600";
|
|
|
|
// 在内容容器中渲染AutoComplete设置
|
|
this.renderAutoCompleteContent(contentContainer);
|
|
}
|
|
|
|
private renderAutoCompleteContent(containerEl: HTMLElement): void {
|
|
const updateSettings = async (update: Partial<InfioSettings>) => {
|
|
await this.plugin.setSettings({
|
|
...this.plugin.settings,
|
|
...update
|
|
});
|
|
|
|
// 只重新渲染 AutoComplete 部分
|
|
if (this.autoCompleteContainer) {
|
|
this.autoCompleteContainer.empty();
|
|
this.renderAutoCompleteContent(this.autoCompleteContainer);
|
|
}
|
|
};
|
|
|
|
const errors = new Map();
|
|
|
|
// AutoComplete base
|
|
// new Setting(containerEl).setName(t('settings.AutoComplete.title')).setHeading();
|
|
this.renderComponent(containerEl,
|
|
<BasicAutoCompleteSettings
|
|
settings={this.plugin.settings}
|
|
updateSettings={updateSettings}
|
|
/>
|
|
);
|
|
|
|
// Preprocessing
|
|
new Setting(containerEl).setName(t('settings.AutoComplete.preprocessing.title')).setHeading();
|
|
this.renderComponent(containerEl,
|
|
<PreprocessingSettings
|
|
settings={this.plugin.settings}
|
|
updateSettings={updateSettings}
|
|
errors={errors}
|
|
/>
|
|
);
|
|
|
|
// Postprocessing
|
|
new Setting(containerEl).setName(t('settings.AutoComplete.postprocessing.title')).setHeading();
|
|
this.renderComponent(containerEl,
|
|
<PostprocessingSettings
|
|
settings={this.plugin.settings}
|
|
updateSettings={updateSettings}
|
|
/>
|
|
);
|
|
|
|
// Trigger
|
|
new Setting(containerEl).setName(t('settings.AutoComplete.trigger.title')).setHeading();
|
|
this.renderComponent(containerEl,
|
|
<TriggerSettingsSection
|
|
settings={this.plugin.settings}
|
|
updateSettings={updateSettings}
|
|
errors={errors}
|
|
/>
|
|
);
|
|
|
|
// Privacy
|
|
new Setting(containerEl).setName(t('settings.AutoComplete.privacy.title')).setHeading();
|
|
this.renderComponent(containerEl,
|
|
<PrivacySettings
|
|
settings={this.plugin.settings}
|
|
updateSettings={updateSettings}
|
|
errors={errors}
|
|
/>
|
|
);
|
|
|
|
// // Danger zone
|
|
// new Setting(containerEl).setName(t('settings.AutoComplete.dangerZone.title')).setHeading();
|
|
// this.renderComponent(containerEl,
|
|
// <DangerZoneSettings
|
|
// settings={this.plugin.settings}
|
|
// updateSettings={updateSettings}
|
|
// onReset={() => {
|
|
// new Notice(t('settings.AutoComplete.dangerZone.resetComplete'));
|
|
// }}
|
|
// />
|
|
// );
|
|
|
|
// // Advanced
|
|
|
|
// if (this.plugin.settings.advancedMode) {
|
|
// new Setting(containerEl).setName(t('settings.AutoComplete.advanced.title')).setHeading();
|
|
// this.renderComponent(containerEl,
|
|
// <AdvancedSettings
|
|
// settings={this.plugin.settings}
|
|
// updateSettings={updateSettings}
|
|
// errors={errors}
|
|
// />
|
|
// );
|
|
// }
|
|
}
|
|
|
|
private renderComponent(containerEl: HTMLElement, component: React.ReactNode) {
|
|
const div = containerEl.createDiv("div");
|
|
const root = createRoot(div);
|
|
root.render(component);
|
|
}
|
|
}
|
|
|
|
class ExcludedFilesModal extends Modal {
|
|
private files: TFile[]
|
|
|
|
constructor(app: App, files: TFile[]) {
|
|
super(app)
|
|
this.files = files
|
|
}
|
|
|
|
onOpen() {
|
|
const { contentEl } = this
|
|
contentEl.empty()
|
|
|
|
this.titleEl.setText(`Excluded Files (${this.files.length})`)
|
|
|
|
if (this.files.length === 0) {
|
|
contentEl.createEl('p', { text: t('settings.RAG.noExcludedFiles') })
|
|
return
|
|
}
|
|
|
|
const list = contentEl.createEl('ul')
|
|
this.files.forEach((file) => {
|
|
list.createEl('li', { text: file.path })
|
|
})
|
|
}
|
|
|
|
onClose() {
|
|
const { contentEl } = this
|
|
contentEl.empty()
|
|
}
|
|
}
|
|
|
|
class IncludedFilesModal extends Modal {
|
|
private files: TFile[]
|
|
private patterns: string[]
|
|
|
|
constructor(app: App, files: TFile[], patterns: string[]) {
|
|
super(app)
|
|
this.files = files
|
|
this.patterns = patterns
|
|
}
|
|
|
|
onOpen() {
|
|
const { contentEl } = this
|
|
contentEl.empty()
|
|
|
|
this.titleEl.setText(`Included Files (${this.files.length})`)
|
|
|
|
if (this.patterns.length === 0) {
|
|
contentEl.createEl('p', {
|
|
text: t('settings.RAG.noInclusionPatterns'),
|
|
})
|
|
return
|
|
}
|
|
|
|
if (this.files.length === 0) {
|
|
contentEl.createEl('p', {
|
|
text: t('settings.RAG.noMatchingFiles'),
|
|
})
|
|
return
|
|
}
|
|
|
|
const list = contentEl.createEl('ul')
|
|
this.files.forEach((file) => {
|
|
list.createEl('li', { text: file.path })
|
|
})
|
|
}
|
|
|
|
onClose() {
|
|
const { contentEl } = this
|
|
contentEl.empty()
|
|
}
|
|
}
|