mirror of
https://github.com/khoaliber/khoj.git
synced 2026-03-02 13:18:18 +00:00
Enable excluding folders to sync from obsidian plugin settings (#1235)
- Add excludeFolders field to KhojSetting interface - Rename 'Sync Folders' to 'Include Folders' for clarity - Add 'Exclude Folders' UI section with folder picker - Filter out excluded folders during content sync - Show file counts when syncing (X of Y files) - Prevent excluding root folder This allows users to exclude specific directories (e.g., Inbox, Highlights) from being indexed, while the existing Include Folders acts as a whitelist. --------- Co-authored-by: Debanjum <debanjum@gmail.com>
This commit is contained in:
@@ -36,6 +36,7 @@ export interface KhojSetting {
|
||||
syncFileType: SyncFileTypes;
|
||||
userInfo: UserInfo | null;
|
||||
syncFolders: string[];
|
||||
excludeFolders: string[];
|
||||
syncInterval: number;
|
||||
autoVoiceResponse: boolean;
|
||||
fileAccessMode: 'none' | 'read' | 'write';
|
||||
@@ -57,6 +58,7 @@ export const DEFAULT_SETTINGS: KhojSetting = {
|
||||
},
|
||||
userInfo: null,
|
||||
syncFolders: [],
|
||||
excludeFolders: [],
|
||||
syncInterval: 60,
|
||||
autoVoiceResponse: true,
|
||||
fileAccessMode: 'read',
|
||||
@@ -273,11 +275,11 @@ export class KhojSettingTab extends PluginSettingTab {
|
||||
this.plugin.restartSyncTimer();
|
||||
}));
|
||||
|
||||
// Add setting to manage sync folders
|
||||
const syncFoldersContainer = containerEl.createDiv('sync-folders-container');
|
||||
new Setting(syncFoldersContainer)
|
||||
.setName('Sync Folders')
|
||||
.setDesc('Specify folders to sync (leave empty to sync entire vault)')
|
||||
// Add setting to manage include folders
|
||||
const includeFoldersContainer = containerEl.createDiv('include-folders-container');
|
||||
new Setting(includeFoldersContainer)
|
||||
.setName('Include Folders')
|
||||
.setDesc('Folders to sync (leave empty to sync entire vault)')
|
||||
.addButton(button => button
|
||||
.setButtonText('Add Folder')
|
||||
.onClick(() => {
|
||||
@@ -285,15 +287,42 @@ export class KhojSettingTab extends PluginSettingTab {
|
||||
if (!this.plugin.settings.syncFolders.includes(folder)) {
|
||||
this.plugin.settings.syncFolders.push(folder);
|
||||
this.plugin.saveSettings();
|
||||
this.updateFolderList(folderListEl);
|
||||
this.updateIncludeFolderList(includeFolderListEl);
|
||||
}
|
||||
});
|
||||
modal.open();
|
||||
}));
|
||||
|
||||
// Create a list to display selected folders
|
||||
const folderListEl = syncFoldersContainer.createDiv('folder-list');
|
||||
this.updateFolderList(folderListEl);
|
||||
// Create a list to display selected include folders
|
||||
const includeFolderListEl = includeFoldersContainer.createDiv('folder-list');
|
||||
this.updateIncludeFolderList(includeFolderListEl);
|
||||
|
||||
// Add setting to manage exclude folders
|
||||
const excludeFoldersContainer = containerEl.createDiv('exclude-folders-container');
|
||||
new Setting(excludeFoldersContainer)
|
||||
.setName('Exclude Folders')
|
||||
.setDesc('Folders to exclude from sync (takes precedence over includes)')
|
||||
.addButton(button => button
|
||||
.setButtonText('Add Folder')
|
||||
.onClick(() => {
|
||||
const modal = new FolderSuggestModal(this.app, (folder: string) => {
|
||||
// Don't allow excluding root folder
|
||||
if (folder === '') {
|
||||
new Notice('Cannot exclude the root folder');
|
||||
return;
|
||||
}
|
||||
if (!this.plugin.settings.excludeFolders.includes(folder)) {
|
||||
this.plugin.settings.excludeFolders.push(folder);
|
||||
this.plugin.saveSettings();
|
||||
this.updateExcludeFolderList(excludeFolderListEl);
|
||||
}
|
||||
});
|
||||
modal.open();
|
||||
}));
|
||||
|
||||
// Create a list to display selected exclude folders
|
||||
const excludeFolderListEl = excludeFoldersContainer.createDiv('folder-list');
|
||||
this.updateExcludeFolderList(excludeFolderListEl);
|
||||
|
||||
let indexVaultSetting = new Setting(containerEl);
|
||||
indexVaultSetting
|
||||
@@ -441,19 +470,52 @@ export class KhojSettingTab extends PluginSettingTab {
|
||||
});
|
||||
}
|
||||
|
||||
// Helper method to update the folder list display
|
||||
private updateFolderList(containerEl: HTMLElement) {
|
||||
// Helper method to update the include folder list display
|
||||
private updateIncludeFolderList(containerEl: HTMLElement) {
|
||||
this.updateFolderList(
|
||||
containerEl,
|
||||
this.plugin.settings.syncFolders,
|
||||
'Including entire vault',
|
||||
async (folder) => {
|
||||
this.plugin.settings.syncFolders = this.plugin.settings.syncFolders.filter(f => f !== folder);
|
||||
await this.plugin.saveSettings();
|
||||
this.updateIncludeFolderList(containerEl);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Helper method to update the exclude folder list display
|
||||
private updateExcludeFolderList(containerEl: HTMLElement) {
|
||||
this.updateFolderList(
|
||||
containerEl,
|
||||
this.plugin.settings.excludeFolders,
|
||||
'No folders excluded',
|
||||
async (folder) => {
|
||||
this.plugin.settings.excludeFolders = this.plugin.settings.excludeFolders.filter(f => f !== folder);
|
||||
await this.plugin.saveSettings();
|
||||
this.updateExcludeFolderList(containerEl);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Shared helper to render a folder list with remove buttons
|
||||
private updateFolderList(
|
||||
containerEl: HTMLElement,
|
||||
folders: string[],
|
||||
emptyText: string,
|
||||
onRemove: (folder: string) => void
|
||||
) {
|
||||
containerEl.empty();
|
||||
if (this.plugin.settings.syncFolders.length === 0) {
|
||||
if (folders.length === 0) {
|
||||
containerEl.createEl('div', {
|
||||
text: 'Syncing entire vault',
|
||||
text: emptyText,
|
||||
cls: 'folder-list-empty'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const list = containerEl.createEl('ul', { cls: 'folder-list' });
|
||||
this.plugin.settings.syncFolders.forEach(folder => {
|
||||
folders.forEach(folder => {
|
||||
const item = list.createEl('li', { cls: 'folder-list-item' });
|
||||
item.createSpan({ text: folder });
|
||||
|
||||
@@ -461,11 +523,7 @@ export class KhojSettingTab extends PluginSettingTab {
|
||||
cls: 'folder-list-remove',
|
||||
text: '×'
|
||||
});
|
||||
removeButton.addEventListener('click', async () => {
|
||||
this.plugin.settings.syncFolders = this.plugin.settings.syncFolders.filter(f => f !== folder);
|
||||
await this.plugin.saveSettings();
|
||||
this.updateFolderList(containerEl);
|
||||
});
|
||||
removeButton.addEventListener('click', () => onRemove(folder));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ export async function updateContentIndex(vault: Vault, setting: KhojSetting, las
|
||||
if (fileTypeToExtension.image.includes(file.extension)) return setting.syncFileType.images;
|
||||
return false;
|
||||
})
|
||||
// Filter files based on specified folders
|
||||
// Filter files based on specified folders (include)
|
||||
.filter(file => {
|
||||
// If no folders are specified, sync all files
|
||||
if (setting.syncFolders.length === 0) return true;
|
||||
@@ -81,12 +81,35 @@ export async function updateContentIndex(vault: Vault, setting: KhojSetting, las
|
||||
return setting.syncFolders.some(folder =>
|
||||
file.path.startsWith(folder + '/') || file.path === folder
|
||||
);
|
||||
})
|
||||
// Filter out excluded folders
|
||||
.filter(file => {
|
||||
// If no folders are excluded, include all files
|
||||
if (setting.excludeFolders.length === 0) return true;
|
||||
// Exclude files in any of the excluded folders
|
||||
return !setting.excludeFolders.some(folder =>
|
||||
file.path.startsWith(folder + '/') || file.path === folder
|
||||
);
|
||||
});
|
||||
|
||||
// Log total eligible files
|
||||
console.log(`Khoj: Found ${files.length} eligible files in vault`);
|
||||
|
||||
let countOfFilesToIndex = 0;
|
||||
let countOfFilesToDelete = 0;
|
||||
lastSync = lastSync.size > 0 ? lastSync : new Map<TFile, number>();
|
||||
|
||||
// Count files that need indexing (modified since last sync or regenerating)
|
||||
const filesToSync = regenerate
|
||||
? files
|
||||
: files.filter(file => file.stat.mtime >= (lastSync.get(file) ?? 0));
|
||||
|
||||
// Show notice with file counts when user triggers sync
|
||||
if (userTriggered) {
|
||||
new Notice(`🔄 Syncing ${filesToSync.length} of ${files.length} files to Khoj...`);
|
||||
}
|
||||
console.log(`Khoj: ${filesToSync.length} files to sync (${files.length} total eligible)`);
|
||||
|
||||
// Add all files to index as multipart form data
|
||||
let fileData = [];
|
||||
let currentBatchSize = 0;
|
||||
@@ -233,8 +256,9 @@ export async function updateContentIndex(vault: Vault, setting: KhojSetting, las
|
||||
if (error_message) {
|
||||
new Notice(error_message);
|
||||
} else {
|
||||
if (userTriggered) new Notice('✅ Updated Khoj index.');
|
||||
console.log(`✅ Refreshed Khoj content index. Updated: ${countOfFilesToIndex} files, Deleted: ${countOfFilesToDelete} files.`);
|
||||
const summary = `Updated ${countOfFilesToIndex}, deleted ${countOfFilesToDelete} files`;
|
||||
if (userTriggered) new Notice(`✅ ${summary}`);
|
||||
console.log(`✅ Refreshed Khoj content index. ${summary}.`);
|
||||
}
|
||||
|
||||
return lastSync;
|
||||
|
||||
Reference in New Issue
Block a user