From 29cddbcb572caa7ebfac9b058a4df40d767cc29d Mon Sep 17 00:00:00 2001 From: dimitrievgs Date: Sat, 27 Sep 2025 23:51:05 +0300 Subject: [PATCH] Add parameters for visible and total items of autocomplete dropdown for @ and # --- src/css/styles.css | 6 +- src/references/AutocompleteManager.ts | 138 +++++++++++++------------- 2 files changed, 76 insertions(+), 68 deletions(-) diff --git a/src/css/styles.css b/src/css/styles.css index bd87b98..e0632f6 100644 --- a/src/css/styles.css +++ b/src/css/styles.css @@ -671,13 +671,17 @@ /* Reference Autocomplete */ .reference-autocomplete { + /* 200px для 5 элементов, возможно, нужно заменить на расчёт с помощью padding и размером шрифта */ + --item-height: 33px; /* высота одного элемента */ + --visible-items: 10; /* количество видимых элементов */ + position: absolute; background: var(--background-primary); border: 1px solid var(--border-color); border-radius: 8px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); z-index: 1000; - max-height: 200px; + max-height: calc(var(--item-height) * var(--visible-items)); overflow-y: auto; min-width: 250px; max-width: 400px; diff --git a/src/references/AutocompleteManager.ts b/src/references/AutocompleteManager.ts index a74cb50..a38d174 100644 --- a/src/references/AutocompleteManager.ts +++ b/src/references/AutocompleteManager.ts @@ -25,6 +25,11 @@ export class AutocompleteManager { private lastCacheUpdate = 0; private readonly CACHE_TTL = 30000; // 30 секунд + /** + * @property {number} MAX_TOTAL_ITEMS Максимальное количество элементов для поиска и кэширования. + */ + static MAX_TOTAL_ITEMS = 20; + constructor(plugin: LLMAgentPlugin) { this.plugin = plugin; this.setupFileWatcher(); @@ -49,13 +54,13 @@ export class AutocompleteManager { const allFiles = this.fileCache.get('all') || []; if (!query.trim()) { - return allFiles.slice(0, 10); // Показываем первые 10 файлов + return allFiles.slice(0, AutocompleteManager.MAX_TOTAL_ITEMS); // Показываем первые 10 файлов } const lowercaseQuery = query.toLowerCase(); return allFiles .filter((item) => item.name.toLowerCase().includes(lowercaseQuery) || item.path.toLowerCase().includes(lowercaseQuery)) - .slice(0, 10); + .slice(0, AutocompleteManager.MAX_TOTAL_ITEMS); } /** @@ -73,13 +78,13 @@ export class AutocompleteManager { const allPrompts = this.promptCache.get('all') || []; if (!query.trim()) { - return allPrompts.slice(0, 10); + return allPrompts.slice(0, AutocompleteManager.MAX_TOTAL_ITEMS); } const lowercaseQuery = query.toLowerCase(); return allPrompts .filter((item) => item.name.toLowerCase().includes(lowercaseQuery) || item.path.toLowerCase().includes(lowercaseQuery)) - .slice(0, 10); + .slice(0, AutocompleteManager.MAX_TOTAL_ITEMS); } /** @@ -107,59 +112,58 @@ export class AutocompleteManager { /** * Обновляет кеш файлов и промптов */ - private async updateCache(): Promise { - const files = this.plugin.app.vault.getMarkdownFiles(); - const fileItems: AutocompleteItem[] = []; - const promptItems: AutocompleteItem[] = []; + private async updateCache(): Promise { + const files = this.plugin.app.vault.getMarkdownFiles(); + const fileItems: AutocompleteItem[] = []; + const promptItems: AutocompleteItem[] = []; - // Нормализуем пути исключенных папок - const normalizedExcludedFolders = this.plugin.settings.excludedFolders - .filter(folder => folder.trim() !== '') - .map(folder => folder.endsWith('/') ? folder : folder + '/') // Убедимся, что заканчивается на слэш - .map(folder => folder.startsWith('/') ? folder.substring(1) : folder); // Удаляем начальный слэш + // Нормализуем пути исключенных папок + const normalizedExcludedFolders = this.plugin.settings.excludedFolders + .filter((folder) => folder.trim() !== '') + .map((folder) => (folder.endsWith('/') ? folder : folder + '/')) // Убедимся, что заканчивается на слэш + .map((folder) => (folder.startsWith('/') ? folder.substring(1) : folder)); // Удаляем начальный слэш - for (const file of files) { - // Пропускаем файлы из исключенных папок - if (this.isFileInExcludedFolder(file, normalizedExcludedFolders)) { - continue; - } + for (const file of files) { + // Пропускаем файлы из исключенных папок + if (this.isFileInExcludedFolder(file, normalizedExcludedFolders)) { + continue; + } - const item: AutocompleteItem = { - name: file.basename, - path: file.path, - type: this.isPromptFile(file) ? 'prompt' : 'file', - icon: this.getFileIcon(file), - file: file - }; + const item: AutocompleteItem = { + name: file.basename, + path: file.path, + type: this.isPromptFile(file) ? 'prompt' : 'file', + icon: this.getFileIcon(file), + file: file + }; - if (item.type === 'prompt') { - promptItems.push(item); - } else { - fileItems.push(item); - } - } + if (item.type === 'prompt') { + promptItems.push(item); + } else { + fileItems.push(item); + } + } - // Сортируем по имени - fileItems.sort((a, b) => a.name.localeCompare(b.name)); - promptItems.sort((a, b) => a.name.localeCompare(b.name)); + // Сортируем по имени + fileItems.sort((a, b) => a.name.localeCompare(b.name)); + promptItems.sort((a, b) => a.name.localeCompare(b.name)); + + this.fileCache.set('all', fileItems); + this.promptCache.set('all', promptItems); + } - this.fileCache.set('all', fileItems); - this.promptCache.set('all', promptItems); - } - /** * Проверяет, является ли файл промптом */ - private isPromptFile(file: TFile): boolean { - const promptsFolder = this.plugin.settings.promptsFolder; - if (!promptsFolder) return false; - - // Нормализуем путь к папке промптов - const normalizedPromptsPath = promptsFolder.replace(/^\/+|\/+$/g, ''); - - return file.path.startsWith(normalizedPromptsPath + '/') || - file.path === normalizedPromptsPath; - } + private isPromptFile(file: TFile): boolean { + const promptsFolder = this.plugin.settings.promptsFolder; + if (!promptsFolder) return false; + + // Нормализуем путь к папке промптов + const normalizedPromptsPath = promptsFolder.replace(/^\/+|\/+$/g, ''); + + return file.path.startsWith(normalizedPromptsPath + '/') || file.path === normalizedPromptsPath; + } /** * Возвращает иконку для файла на основе Obsidian иконок @@ -214,23 +218,23 @@ export class AutocompleteManager { ); } - /** - * Проверяет, находится ли файл в одной из исключенных папок. - * @param file - файл Obsidian. - * @param excludedFolders - массив нормализованных путей исключенных папок. - * @returns true, если файл находится в исключенной папке. - */ - private isFileInExcludedFolder(file: TFile, excludedFolders: string[]): boolean { - const filePath = file.path.toLowerCase(); // Для регистронезависимого сравнения - - for (const folder of excludedFolders) { - const normalizedFolder = folder.toLowerCase(); - // Проверяем, начинается ли путь файла с пути исключенной папки - // Или если файл находится прямо в корне исключенной папки (н.п. "folder/file.md") - if (filePath.startsWith(normalizedFolder) ) { - return true; - } - } - return false; - } + /** + * Проверяет, находится ли файл в одной из исключенных папок. + * @param file - файл Obsidian. + * @param excludedFolders - массив нормализованных путей исключенных папок. + * @returns true, если файл находится в исключенной папке. + */ + private isFileInExcludedFolder(file: TFile, excludedFolders: string[]): boolean { + const filePath = file.path.toLowerCase(); // Для регистронезависимого сравнения + + for (const folder of excludedFolders) { + const normalizedFolder = folder.toLowerCase(); + // Проверяем, начинается ли путь файла с пути исключенной папки + // Или если файл находится прямо в корне исключенной папки (н.п. "folder/file.md") + if (filePath.startsWith(normalizedFolder)) { + return true; + } + } + return false; + } }